Reputation: 590
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
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
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
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