0
votes

Can anyone explain this to me? (using latest libxslt):

 <a><xsl:copy-of select="(@attrib|exsl:node-set(0))"/></a>
 <b><xsl:copy-of select="(@attrib|exsl:node-set(0))[position()=1]"/></b>

 <x><xsl:copy-of select="(@attrib|exsl:node-set(0))[1]"/></x>
 <xsl:variable name="value" select="@attrib"/>
 <y><xsl:copy-of select="($value|exsl:node-set(0))[1]"/></y>

Result (for @attrib = 1 at current node):

 <a attrib="1">0</a>
 <b attrib="1"/>

 <x>0</x>
 <y attrib="1"/>

<a> and <b> show expected behavior.
<x> is IMHO incorrect.
But why does putting @attrib into a variable "fix" it for <y>?

BTW: Everything is correct when @attrib is not present. The copy-of used here is for debugging; original usage converts the XPath result to a number, and a missing attribute shall not lead to NaN, but a certain default value.

2
I strongly recommend not to apply ext:node-set() on a scalar (non-node-set or non-RTF) argument. In this case the created node is in its separate document and there is no ordering defined between this node and nodes of other documents -- meaning that it may come as first or last, and this is unpredictable. Also note, that no other xxx:node-set() extension accepts a scalar argument. Even some implementations of ext:node-set() reject a scalar argument as error: Saxon 6.5.4: "Error at xsl:copy-of on line 7 of file:/(Untitled): exslt:node-set(): argument must be a node-set or tree" - Dimitre Novatchev
@DimitreNovatchev fair point. Still, using <xsl:variable name="default">0</xsl:variable> and $default instead of 0 does not change much. - smilingthax
@_smilingthax: Yes, nothing is changed because the child of the xsl:variable resides in the stylesheet document and the arrtib attribute resides inthe source XML document. There is no ordering defined between nodes that reside in two different XML documents. XPath 1.0 doesn't have sequences, as XPath 2.0 and as you have found, sequences cannot be modelled easily. - Dimitre Novatchev
@DimitreNovatchev: so you're basically saying, what I try to do depends on undefined behavior in xslt 1.1? Is there any specified difference between [1] and [position()=1] (i.e. <b> and <x>)? - smilingthax
@_smilingthax: To start with, there never has been an XSLT 1.1 W3C Recommendation. I am speaking only about XSLT 1.0. As for [1] and [position() = 1] the former must be interpreted as a shorthand for the latter. This is as per XPath 1.0 spec: "the result is a number, the result will be converted to true if the number is equal to the context position and will be converted to false otherwise; Thus a location path para[3] is equivalent to para[position()=3]. " see: w3.org/TR/xpath/#predicates - Dimitre Novatchev

2 Answers

1
votes

This version of exsl:node-set() presumably creates a node that is in a different tree from from the node @attrib. When two nodes are in different trees, it is implementation-dependent which of them comes first in document order. The construct (X|Y)[position()=1] (or equivalently (X|Y)[1]) selects whichever of X and Y comes first in document order. Therefore it's essentially unpredictable whether it selects X or Y.

1
votes

From a comment of the OP:

Ok, that means my only option is using <xsl:choose>..., correct?

While the way you attempt it produces unpredictable results due to reasons explained both by @Michael Kay and by me in my comments, there are still ways to do what you want (produce either the value of the attrib attribute or a default value (0):

concat(@attrib,
       substring(0, 2 -not(@attrib))
       )

This produces the value of the attrib attribute (if this attribute is present) or (if not present) the default value 0.

Complete XSLT solution:

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

 <xsl:template match="x">
  <xsl:value-of select=
   "concat(@attrib,
           substring('0', 2 -not(@attrib))
           )"/>

==========
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the following XML document:

<t>
 <x attrib="abc"/>
 <x/>
</t>

the wanted, correct result is produced:

 abc

==========

 0

==========