XSL result tree fragments keep complicating things
Thursday, April 7th, 2011 12:17 amIf a variable has a select attribute, then its value will always be one of the four basic XPath data-types (string, number, boolean, node-set).
If a variable has empty content and does not have a select attribute, then the value of the variable is an empty string. Thus
A variable with non-empty content ALWAYS returns a value of type Result Tree Fragment (RTF).
A RTF is "treated equivalently to a node-set that contains just a single root node."
I don't understand what is meant by "treated equivalently", considering that you can't perform XPath expressions on a RTF.
However, for a variable of type RTF, this kind of test will always evaluate to true:
as a single root node is a non-empty nodeset, and non-empty nodesets get converted to a boolean value of True. Therefore when $RTF is converted to a boolean, it evaluates to true.
That is the case even when the variable is defined like this:
For this variable, both of the following evaluate to true! (even though an empty string would normally be equivalent to false)
<xsl:value-of> always returns the *string-value* of the XPath expression, boolean or otherwise.
(see http://cygwin.com/ml/xsl-list/2000-10/msg00198.html)
In this case, $RTF is *not* a string - it is still a result tree fragment.
But when a RTF is compared to a string, apparently the RTF is converted to a string.
So this condition evaluates to true:
and this evaluates to false:
...
Distinguishing between an empty node-set and an empty string...
If your XSL template has an optional parameter which may or may not be passed,
and which may be an empty or non-empty node-set when it is passed,
then it is hard to distinguish between the parameter not having been passed, versus an empty node-set having been passed, as in both cases, comparing the parameter to '' returns true.
One way around this problem is to give the parameter a default non-empty string value which the passed node-set should never contain, and then to check if the parameter's value is other than that default value.
If the parameter is passed and non-empty, then it won't equal the default value.
If the parameter is passed and is empty, then it also won't equal the default value.
If a variable has empty content and does not have a select attribute, then the value of the variable is an empty string. Thus
<xsl:variable name="x"></xsl:variable>is equivalent to
<xsl:variable name="x" select="''" />
A variable with non-empty content ALWAYS returns a value of type Result Tree Fragment (RTF).
A RTF is "treated equivalently to a node-set that contains just a single root node."
I don't understand what is meant by "treated equivalently", considering that you can't perform XPath expressions on a RTF.
However, for a variable of type RTF, this kind of test will always evaluate to true:
<xsl:if test="$RTF">
as a single root node is a non-empty nodeset, and non-empty nodesets get converted to a boolean value of True. Therefore when $RTF is converted to a boolean, it evaluates to true.
That is the case even when the variable is defined like this:
<xsl:variable name="RTF"> <xsl:if test="something that evaluates to false"> ... </xsl:if> </xsl:variable>
For this variable, both of the following evaluate to true! (even though an empty string would normally be equivalent to false)
<xsl:if test="$RTF"> <xsl:if test="$RTF = ''">
<xsl:value-of> always returns the *string-value* of the XPath expression, boolean or otherwise.
(see http://cygwin.com/ml/xsl-list/2000-10/msg00198.html)
<xsl:variable name="RTF"> <xsl:value-of select="'abc'" /> </xsl:variable>
In this case, $RTF is *not* a string - it is still a result tree fragment.
But when a RTF is compared to a string, apparently the RTF is converted to a string.
So this condition evaluates to true:
<xsl:if test="$RTF = 'abc'">
and this evaluates to false:
<xsl:if test="$RTF = 'abcd'">
...
Distinguishing between an empty node-set and an empty string...
If your XSL template has an optional parameter which may or may not be passed,
and which may be an empty or non-empty node-set when it is passed,
then it is hard to distinguish between the parameter not having been passed, versus an empty node-set having been passed, as in both cases, comparing the parameter to '' returns true.
One way around this problem is to give the parameter a default non-empty string value which the passed node-set should never contain, and then to check if the parameter's value is other than that default value.
If the parameter is passed and non-empty, then it won't equal the default value.
If the parameter is passed and is empty, then it also won't equal the default value.
<xsl:param name="inputNode" select="'_not_passed_'" /> <xsl:if test="not($inputNode = '_not_passed_')"> <!-- if true, this means the parameter *was* passed. -->