fawick
fawick

Reputation: 590

Consequence of xsl:value-of as child of xsl:variable

What is the differences between these three blocks in terms of side-effects when $world is a list of elements? I am seeing a different behaviour between the first and the third and cannot get my head around it.

<xsl:variable name="hello" select="$world" />

<xsl:variable name="hello">
    <xsl:value-of select="$world" />
</xsl:variable>

<xsl:variable name="hello">
    <xsl:choose>
        <xsl:when test="$something=true()">
            <xsl:value-of select="$world" />
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$world" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Edit 1: I want to process $hello in a <xsl:for-each select="$hello">. With the third block above the <xsl:for-each> has only one item to process that contains the joined contents of $world. Why is that?

Upvotes: 2

Views: 1188

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

  <xsl:variable name="hello">
      <xsl:choose>
          <xsl:when test="$something=true()">
              <xsl:value-of select="$world" />
          </xsl:when>
          <xsl:otherwise>
              <xsl:value-of select="$world" />
          </xsl:otherwise>            
         </xsl:choose>        
    </xsl:variable>

I want to process $hello in a <xsl:for-each select="$hello">. With the block above the <xsl:for-each> has only one item to process that contains the joined contents of $world. Why is that?

The variable named $hello contains the string value of $world. This is by definition how <xsl:value-of> behaves in XSLT 1.0.

You haven't shown us how $world is defined, but if it contains a single element or a whole document tree, then (again) by definition, its string value is the concatenation (in document order) of all of its descendents - text nodes.

This is exactly what you are seeing.

The situation will be different if insead of:

<xsl:value-of select="$world" />

you use:

<xsl:copy-of select="$world" />

This copies the whole subtree whose root is the element (or root node / in the case when $world contains a complete document) contained in $world.

However, in XSLT 1.0 this creates the so called RTF (Result Tree Fragment) and by definition one cannot use an RTF as a location step in XPath (1.0) expression.

One must first convert this to a regular tree (document node) using a vendor-supplied extension function that most often has the local-name node-set but is in a vendor-specific namespace.

A typical example is:

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

 <xsl:variable name="vWorld" select="/*"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfHello">
    <xsl:copy-of select="$vWorld"/>
  </xsl:variable>

  <xsl:variable name="vHello" select=
   "ext:node-set($vrtfHello)/*"/>

  <xsl:copy-of select="$vHello/*[3]"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the following XML document:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

the result is (as expected):

<num>03</num>

Here we use the ext:node-set() extension function in the namespace "http://exslt.org/common" as specified by the EXSLT vendor-independent library. Most XSLT 1.0 processors support EXSLT and using its node-set() extension function doesn't decrease the degree of portability of an XSLT application accross all such XSLT processors.

Upvotes: 0

Michael Kay
Michael Kay

Reputation: 163468

While all three examples are valid in both XSLT 1.0 and XSLT 2.0, the way the semantics are described is very different in the two specs; also when $value contains multiple nodes, the effect of <xsl:value-of select="$value"/> depends on whether the stylesheet specifies version="1.0" or version="2.0".

The main things to remember, that apply to both versions, are (a) xsl:value-of creates a text node by converting whatever it selects into a string, and (b) xsl:variable with contained instructions (and no "as" attribute) creates a new tree rooted at a document node.

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167696

The first xsl:variable will have the same value and type as $world. The second is a result tree fragment with a single text node of the string value of $world. The third is also a result tree fragment with a single text node.

I guess you want either

  <xsl:variable name="hello" select="if (condition) then $world else $foo"/>

in XSLT 2.0 and then your for-each select="$hello" would work as you want or in XSLT 1.0 plus EXSLT common you want

<xsl:variable name="hello">
  <xsl:choose>
    <xsl:when test="condition">
      <xsl:copy-of select="$world"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="$foo"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:for-each select="exsl:node-set($hello)/*">...</xsl:for-each>

Upvotes: 2

Related Questions