Wojciech Owczarczyk
Wojciech Owczarczyk

Reputation: 5735

Dynamic XSLT for-each iteration

I have the following XML:

<bookstore>
        <author name="King">
              <book id="book1"><title>Title1</title></book>
              <book id="book2"><title>Title2</title></book>
              <book id="book3"><title>Title3</title></book>
              <book id="book4"><title>Title4</title></book>
        </author>
</bookstore>

then I have an XSLT template, such as:

<xsl:template>
    <xsl:param name="booksPath" select="'bookstore/author/book'"/>
    <xsl:for-each select="*[local-name() = $booksPath]">
         <p><xsl:value-of select="title" /></p>
    </xsl:for-each>
</xsl:template>

and this for-each loop does not work. I want to iterate on books What am I doing wrong?

Upvotes: 0

Views: 2463

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

While full dynamic XPath evaluation isn't part of either XSLT 1.0/XPath 1.0 or XSLT 2.0/XPath 2.0, one can produce an XSLT 1.0 implementation of something that would work in a rather limited way:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
   <xsl:variable name="vrtfEvalResult">
     <xsl:call-template name="eval">
       <xsl:with-param name="pPath" select="'/bookstore/author/book'"/>
     </xsl:call-template>
   </xsl:variable>

  <xsl:for-each select="ext:node-set($vrtfEvalResult)/*">
    <p><xsl:value-of select="title" /></p>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>

 <xsl:template match="Path" name="eval">
  <xsl:param name="pPath" select="."/>
  <xsl:param name="pContext" select="/"/>

  <xsl:choose>
   <!-- If there is something to evaluate -->
   <xsl:when test="string-length($pPath) >0">
      <xsl:variable name="vPath" select=
          "substring($pPath,2)"/>

      <xsl:variable name="vNameTest">
       <xsl:choose>
        <xsl:when test="not(contains($vPath, '/'))">
         <xsl:value-of select="$vPath"/>
        </xsl:when>
        <xsl:otherwise>
         <xsl:value-of select=
             "substring-before($vPath, '/')"/>
        </xsl:otherwise>
       </xsl:choose>
      </xsl:variable>

      <xsl:call-template name="eval">
       <xsl:with-param name="pPath" select=
         "substring-after($pPath, $vNameTest)"/>
       <xsl:with-param name="pContext" select=
        "$pContext/*[name()=$vNameTest]"/>
      </xsl:call-template>
  </xsl:when>
  <!-- Otherwise we have evaluated completely the path -->
  <xsl:otherwise>
   <xsl:copy-of select="$pContext"/>
  </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<bookstore>
    <author name="King">
        <book id="book1">
            <title>Title1</title>
        </book>
        <book id="book2">
            <title>Title2</title>
        </book>
        <book id="book3">
            <title>Title3</title>
        </book>
        <book id="book4">
            <title>Title4</title>
        </book>
    </author>
</bookstore>

the wanted, correct result is produced:

<p>Title1</p>
<p>Title2</p>
<p>Title3</p>
<p>Title4</p>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167571

If you really need or want to use dynamic XPath evaluation from a string then you need an extension function like saxon:evaluate e.g. <xsl:for-each select="saxon:evaluate($booksPath)" xmlns:saxon="http://saxon.sf.net/">...</xsl:for-each>.

Upvotes: 2

Related Questions