I'm using the xsltproc
command and getting unexpected output from a predicate using the number()
function. I'm trying to use number()
's output as a boolean, with 0
treated as false and all other values treated as true. The goal is to select all elements with nonzero @original
attribute.
It's easiest to explain via a reproducer.
Reproducer:
<top>
<constraint>
<lifetime>
<rule id="rule1" original="1"/>
<rule id="rule2" original="1"/>
</lifetime>
</constraint>
<constraint>
<lifetime>
<rule id="rule3" original="3"/>
<rule id="rule4" original="0"/>
<rule id="rule5"/>
</lifetime>
</constraint>
</top>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/|@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="top">
<xsl:for-each select="constraint/lifetime/rule[number(@original)]">
<item>
<xsl:copy-of select="."/>
</item>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<item>
<rule id="rule1" original="1"/>
</item>
<item>
<rule id="rule1" original="1"/>
</item><item>
<rule id="rule2" original="1"/>
</item><item>
<rule id="rule3" original="3"/>
</item>
rule1
, rule2
, and rule3
should be included in the output because their @original
values are nonzero. rule4
and rule5
should be excluded because @original
is 0 and unset, respectively.
If I change from number(@original)
to number(@original) = 1
or @original = '1'
, I get the expected results. I also get the expected result if I use an if
within the for-each
instead of using an XPath predicate:
<xsl:for-each select="constraint/lifetime/rule">
<xsl:if test="number(@original)">
<item>
<xsl:copy-of select="."/>
</item>
</xsl:if>
</xsl:for-each>
Edited to clarify the way I'm trying to use number()
and to add rule
elements that should not be included in the output, per feedback in the comments. My question has already been answered satisfactorily.
When a predicate expression evaluates to a number, it is considered to be true if the number is equal to the context position. Your expression:
constraint/lifetime/rule[number(@original)]
is equivalent to:
constraint/lifetime/rule[position() = number(@original)]
and therefore evaluates as true only for those elements that are the first rule
child of their parent lifetime
.
See: https://www.w3.org/TR/1999/REC-xpath-19991116/#predicates