Question

I'm trying to generate an EPUB3 Navigation Document through XSLT from an XML file.

Sample XML:

<div class="toc" id="s1">
    <p class="toc-title">Detailed Contents</p>
    <ul class="toc">
        <li class="toc-item">
            <a class="ref-chap" id="a1" href="#a1">Preface</a>
        </li>
        <li class="book-section">
            <a class="ref-chap" id="a2" href="#a2">
                <b>Part I.</b>
            </a>
            <ul class="book-section">
                <li class="toc-item">
                    <a class="ref-chap" id="a3" href="#a3">
                        <b>Chapter 1</b>
                    </a>
                    <ul class="chapter-section">
                        <li class="toc-item">
                            <a class="ref-chap" id="a4" href="#a4">
                                <b>Sub Chapter a</b>
                            </a>
                            <ul class="chapter-subsection">
                                <li class="toc-item">
                                    <a class="ref-chap" id="a5" href="#a5">Sub Chapter B</a>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>
            <ul class="book-section">
                <li class="toc-item">
                    <a class="ref-chap" id="a6" href="#a6">
                        <b>Chapter 2</b>
                    </a>
                    <ul class="chapter-section">
                        <li class="toc-item">
                            <a class="ref-chap" id="a7" href="#a7">
                                <b>Sub Chapter A</b>
                            </a>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>

Needed output:

<nav xmlns:epub="http://www.idpf.org/2007/ops" epub:type="toc" id="toc">
    <h1>Detailed Contents</h1>
    <ol>
        <li><a href="a1">Preface: To Our Readers</a></li>
        <li><a href="a2"><b>Part I</b></a>
            <ol>
                <li><a href="a3"><b>Chapter 1</b></a>
                    <ol>
                        <li><a href="a4"><b>Sub Chapter A</b></a>
                            <ol>
                                <li><a href="a5">Sub Chapter B</a></li>
                            </ol>
                        </li>
                    </ol>
                </li>
                <li><a href="a6"><b>Chapter 2</b></a>
                    <ol>
                        <li><a href="a7"><b>Sub Chapter A</b></a></li>
                    </ol>
                </li>
            </ol>
        </li>
    </ol>
</nav>

My problem area is the <ol> child of Part I, I need it to close AFTER Chapter 2, but it is closing after the end of Chapter 1 instead with the following XSLT.

My XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:epub="http://www.idpf.org/2007/ops"
    exclude-result-prefixes="xs epub"
    version="2.0">

    <xsl:output method="xhtml" include-content-type="no" xpath-default-namespace="http://www.w3.org/1999/xhtml" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="div[@class='toc']">
        <xsl:element name="nav">
            <xsl:attribute name="epub:type" select="'toc'"/>
            <xsl:attribute name="id" select="'toc'"/>
            <xsl:element name="h1">
                <xsl:value-of select="//div[@class='toc']/p[@class='toc-title']"/>
            </xsl:element>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="div[@class='toc']//ul[@class='toc'] | div[@class='toc']//ul[@class='book-section'] | div[@class='toc']//ul[@class='chapter-section' or @class='chapter-subsection']">
        <xsl:element name="ol">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="div[@class='toc']//li[@class='toc-item']">
        <xsl:element name="li">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="div[@class='toc']//li[@class='book-section']">
        <xsl:element name="li">
            <xsl:apply-templates xml:space="default"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="a[@class='ref-chap']">
        <xsl:element name="a">
            <xsl:attribute name="href" select="concat(substring-after(@href, '#'),'.xhtml')"/>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template priority="1" match="ul[@class='chapter-section' or @class='chapter-subsection']//a[@class='ref-chap']">
        <xsl:variable name="pagenumber" select="following-sibling::a[@class='page-ref']"/>
        <xsl:element name="a">
            <xsl:attribute name="href" select="concat(substring-after(ancestor::ul[@class='chapter-section'][1]/preceding-sibling::a[@class='ref-chap'][1]/@href, '#'),'.xhtml','#page',$pagenumber)"/>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Gives me a nonvalid EPUB3 nav doc:

<nav xmlns:epub="http://www.idpf.org/2007/ops" epub:type="toc" id="toc">
   <h1>Detailed Contents</h1>Detailed Contents
   <ol>
      <li>
         <a href="a1.xhtml">Preface</a>
      </li>
      <li>
         <a href="a2.xhtml">Part I.</a>
         <ol>
            <li>
               <a href="a3.xhtml">Chapter 1</a>
               <ol>
                  <li>
                     <a href="a3.xhtml#page">Sub Chapter a</a>
                     <ol>
                        <li>
                           <a href="a3.xhtml#page">Sub Chapter B</a>
                        </li>
                     </ol>
                  </li>
               </ol>
            </li>
         **</ol>
         <ol>**
            <li>
               <a href="a6.xhtml">Chapter 2</a>
               <ol>
                  <li>
                     <a href="a6.xhtml#page">Sub Chapter A</a>
                  </li>
               </ol>
            </li>
         </ol>
      </li>
   </ol>
</nav>

Emphasis ** was added to indicate these tags are where the problem is. They should not be in the output.

Was it helpful?

Solution

What you want to do is group together the ul[@class='book-section'].

There are different ways to do that, one would be to use for-each-group in the parent template of li[@class='book-section'] so you could change that template to:

<xsl:template match="div[@class='toc']//li[@class='book-section']">
    <xsl:element name="li">
        <xsl:apply-templates select="xhtml:a[@class='ref-chap']"/>
        <xsl:for-each-group select="ul[@class='book-section']" group-by="@class">
            <ol>
                <xsl:apply-templates select="current-group()/node()"/>
            </ol>
        </xsl:for-each-group> 
    </xsl:element>
</xsl:template>

Another way would be to only match the first of the ul[@class='book-section'] but then apply this to the children of the following, too, like this:

<xsl:template match="div[@class='toc']//ul[@class='book-section'][1]">
    <xsl:element name="ol">
        <xsl:apply-templates select="* | following-sibling::ul[@class='book-section']/*"/>
    </xsl:element>
</xsl:template>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top