Search code examples
xslt-1.0xsl-variable

Using xsl:value-of in for-each loop over a xsl:variable


This is a sequel to the question in entry 25317199.

In the 25317199 post, the data has 2 blocks, i.e., Schools and FamilySmith. Data in FamilySmith is used as the key to retrieve the data in Schools.

Now, in this case, the data is split in that FamilySmith is now defined as a variable inside the stylesheet, as illustrated below:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl"
    version="1.0">

    <xsl:variable name="FamilySmith">
        <Children>
            <Child>
                <Name>Thomas</Name>
                <School_Id>5489</School_Id>
            </Child>
            <Child>
                <Name>Andrew</Name>
                <School_Id>7766</School_Id>
            </Child>
        </Children>
    </xsl:variable>

    <xsl:template match="/Doc">
        <xsl:for-each select="exsl:node-set($FamilySmith)/Children/Child">
            <xsl:text>&#xa;</xsl:text>
            <xsl:value-of select="Name"/>
            <xsl:text> goes to (school's name here) </xsl:text>
            <xsl:value-of select="/Doc/Schools/School[Id = current()/School_Id]/Name"/>
            <xsl:text> at (school's address here) </xsl:text>
            <xsl:value-of select="/Doc/Schools/School[Id = current()/School_Id]/Address"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

This is applied to the XML data below:

<Doc>
    <Schools>
        <School>
            <Id>5489</Id>
            <Name>St Thomas</Name>
            <Address>High Street, London, England</Address>
        </School>
        <School>
            <Id>7766</Id>
            <Name>Anderson Boys School</Name>
            <Address>Haymarket, Edinborough</Address>
        </School>
    </Schools>
</Doc>

Both retrievals of school's name and school's address produce empty strings, as shown below.

Thomas goes to (school's name here)  at (school's address here) 
Andrew goes to (school's name here)  at (school's address here) 

I have used the advice given in the previous posting 25317199, i.e., using current() to identify "current context node from outside the predicate." But it seems that the problem lies elsewhere. Please advise. Many thanks.


Solution

  • The problem is that absolute paths starting with / resolve relative to the root node of the tree containing the current context node, which is not necessarily the input XML document. Inside

    <xsl:for-each select="exsl:node-set($FamilySmith)/Children/Child">
    

    the path /Doc/Schools is looking for the Doc element inside the temporary document derived from $FamilySmith (so the value-of instructions select nothing). You need to preserve the context node from outside the for-each in another variable:

    <xsl:template match="/Doc">
        <xsl:variable name="doc" select="." />
        <xsl:for-each select="exsl:node-set($FamilySmith)/Children/Child">
            <xsl:text>&#xa;</xsl:text>
            <xsl:value-of select="Name"/>
            <xsl:text> goes to (school's name here) </xsl:text>
            <xsl:value-of select="$doc/Schools/School[Id = current()/School_Id]/Name"/>
            <xsl:text> at (school's address here) </xsl:text>
            <xsl:value-of select="$doc/Schools/School[Id = current()/School_Id]/Address"/>
        </xsl:for-each>
    </xsl:template>