Search code examples
xslt-2.0xpath-2.0

Keeping track of an iterator through a nested xsl:for-each


I want to take the following input:

<test>
    <a>
        <b />
        <b />
    </a>
    <a>
        <b />
        <b />
    </a>
</test>

And create the following output using XSLT 2.0:

<items>
    <item num="1">
        <item num="2"/>
        <item num="3"/>
    </item>
    <item num="4">
        <item num="5"/>
        <item num="6"/>
    </item>
</items>

I know this is wrong, but for a starting point, here's my current XSLT:

<xsl:template match="test">
    <items>
        <xsl:for-each select="a">
            <item num="{position()}">
                <xsl:for-each select="b">
                    <item num="{position()}"/>
                </xsl:for-each>
            </item>
        </xsl:for-each>
    </items>
</xsl:template>

This is clearly not the way to do it, because position() only considers elements in the same level. But how would I do this?


Solution

  • This transformation:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
      <xsl:template match="/*">
        <items><xsl:apply-templates/></items>
      </xsl:template>
    
      <xsl:template match="a|b">
        <xsl:variable name="vNum">
          <xsl:number level="any" count="a|b"/>
        </xsl:variable>
        <item num="{$vNum}">
          <xsl:apply-templates/>
        </item>
      </xsl:template>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <test>
        <a>
            <b />
            <b />
        </a>
        <a>
            <b />
            <b />
        </a>
    </test>
    

    produces the wanted, correct result:

    <items>
       <item num="1">
          <item num="2"/>
          <item num="3"/>
       </item>
       <item num="4">
          <item num="5"/>
          <item num="6"/>
       </item>
    </items>