xsltxslt-1.0exslt

xslt 1.0 node-set not looping


I have an xsl:variable that contains a list of nodes. When I try to loop over them with for-each, I get no results. I'm using saxon655 and java 1.8.0_181.

Here is the xslt:

  <?xml version="1.0"?>
  <xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml"
                  xmlns:exsl="http://exslt.org/common"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  exclude-result-prefixes="exsl"
                  version="1.0">

    <xsl:variable name="products">
        <array>
        <item><name>Scooby</name><value>Doo</value></item>
        <item><name>snack</name><value>cookies</value></item>
        </array>
    </xsl:variable>

      <xsl:template match="/">
        <xsl:for-each select="exsl:node-set($products)">
          <xsl:message>LOOP</xsl:message>
          <xsl:value-of select=".//name" />
        </xsl:for-each>
      </xsl:template>

  </xsl:stylesheet>

The xml:

<?xml version='1.0' encoding='UTF-8'?>
<book>
   text
</book>

Finally, my command:

/usr/bin/java -cp /usr/local/share/saxon/saxon.jar com.icl.saxon.StyleSheet test.xml test_run.xsl

When I run the command I receive one line of output LOOP.

I expected to get the message and the value for name once for each item in the variable array.


Solution

  • Doing exsl:node-set($products) returns a single document node, which contains the rest of the XML in your variable, so what you need to do is this...

    <xsl:for-each select="exsl:node-set($products)/array/item">
    

    However, this won't work immediately, because you defined a default namespace declaration in your XSLT (xmlns="http://www.w3.org/1999/xhtml"). This means the elements within the variable, being un-prefixed, will be in that namespace.

    So, unless you have a reason to have array and item in a namespace, declare the variable like so

    <xsl:variable name="products" xmlns="">
    

    Try this XSLT

    <xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml"
                  xmlns:exsl="http://exslt.org/common"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  exclude-result-prefixes="exsl"
                  version="1.0">
    
      <xsl:variable name="products" xmlns="">
        <array>
        <item><name>Scooby</name><value>Doo</value></item>
        <item><name>snack</name><value>cookies</value></item>
        </array>
      </xsl:variable>
    
      <xsl:template match="/">
        <xsl:for-each select="exsl:node-set($products)/array/item">
          <xsl:value-of select="name" />
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    

    Alternatively, if you need want array and item in the namespace though, you could handle them like so:

    <xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml"
                    xmlns:xhtml="http://www.w3.org/1999/xhtml"
                      xmlns:exsl="http://exslt.org/common"
                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                      exclude-result-prefixes="exsl"
                      version="1.0">
    
        <xsl:variable name="products">
          <array>
            <item><name>Scooby</name><value>Doo</value></item>
            <item><name>snack</name><value>cookies</value></item>
          </array>
        </xsl:variable>
    
        <xsl:template match="/">
          <xsl:for-each select="exsl:node-set($products)/xhtml:array/xhtml:item">
            <xsl:value-of select="xhtml:name" />
          </xsl:for-each>
        </xsl:template>
    </xsl:stylesheet>