Search code examples
xmlxsltpositionrelative-pathancestor

xsl finding relative position to ancestor node


Have some problems to get the position of childs division relative to the section node. Here is my XML file:

<document>
    <section>
        <division>text of division</division>
        <division>text of division</division>
        <division>text of division</division>
    </section>
    <section>
        <division>text of division</division>
        <division>text of division</division>
        <division>text of division</division>
    </section>
</document>

And here is my XSL:

<xsl:template match="/">
   <xsl:for-each select="//section">
       <div class="section">
           <xsl:text>section </xsl:text>
           <xsl:value-of select="position ()"/>
           <xsl:apply-templates/>
       </div>
    </xsl:for-each>
</xsl:template>

<xsl:template match="division">
    <div class="division">
        <xsl:text>division </xsl:text>
        <xsl:value-of select='count(../preceding-sibling::section)+1' /><xsl:text>.</xsl:text>
        <xsl:value-of select="position ()"/>
        <xsl:apply-templates/>
    </div>
</xsl:template>

And I would like this kind of output where the position depend to section:

section 1
division 1.1 
division 1.2
division 1.3
section 2
division 2.1
division 2.2
division 2.3

And not this one:

section 1
division 1.2 
division 1.4
division 1.6
section 2
division 2.2
division 2.4
division 2.6

It seems to be something like ../section[position()] or ../ancestor/position() but it's wrong… Thanks !


Solution

  • Instead of position(), try using xsl:number instead.

    Example:

    XML Input

    <document>
        <section>
            <division>text of division</division>
            <division>text of division</division>
            <division>text of division</division>
        </section>
        <section>
            <division>text of division</division>
            <division>text of division</division>
            <division>text of division</division>
        </section>
    </document>
    

    XSLT 1.0

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="text()"/>
    
        <xsl:template match="section|division">
            <xsl:value-of select="concat(name(),' ')"/>
            <xsl:number level="multiple" count="section|division"/>
            <xsl:text>&#xA;</xsl:text>
            <xsl:apply-templates/>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Output

    section 1
    division 1.1
    division 1.2
    division 1.3
    section 2
    division 2.1
    division 2.2
    division 2.3
    

    EDIT: Here's another example that's closer to what you were doing in your XSLT. (I added <p> elements to wrap the text though.

    XSLT 1.0

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:template match="text()"/>
    
        <xsl:template match="section|division">
            <div class="{name()}">
                <p>
                    <xsl:value-of select="concat(name(),' ')"/>
                    <xsl:number level="multiple" count="section|division"/>             
                </p>
                <xsl:apply-templates/>              
            </div>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Output

    <div class="section">
       <p>section 1</p>
       <div class="division">
          <p>division 1.1</p>
       </div>
       <div class="division">
          <p>division 1.2</p>
       </div>
       <div class="division">
          <p>division 1.3</p>
       </div>
    </div>
    <div class="section">
       <p>section 2</p>
       <div class="division">
          <p>division 2.1</p>
       </div>
       <div class="division">
          <p>division 2.2</p>
       </div>
       <div class="division">
          <p>division 2.3</p>
       </div>
    </div>