Using position() to select a specific node while in a for-each loop in xslt not returning expected results
xslt, xslt-1.0
Solution
<script type="text/javascript">
// Always returns false
console.log("Dynamic position " +
<xsl:value-of select="position()" /> +
" IsDefault: " +
( <xsl:value-of select="/root/MainProducts/MainProduct[position()]/Published" />
== 1 ? "true" : "false" ));
</script>
In the above code `MainProduct[position()]` is equivalent to `MainProduct[true()]` )don't forget that `position()` is context-sensitive!
So, you are actually evaluating:
<xsl:value-of select="/root/MainProducts/MainProduct/Published" />
and this outputs always the string value of the first `Published` element that is selected by the XPath expression -- it happens to be `0`.
Correct code:
<xsl:variable name="vPos" select="position()"/>
<script type="text/javascript">
// Always returns false
console.log("Dynamic position " +
<xsl:value-of select="position()" /> +
" IsDefault: " +
( <xsl:value-of select=
"/root/MainProducts/MainProduct[position()=$vPos]/Published" />
== 1 ? "true" : "false" ));
</script>
Here is a complete transformation:
<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="/">
<xsl:for-each select="/*/SubProducts/SubProduct">
<xsl:variable name="vPos" select="position()"/>
<script type="text/javascript">
console.log("Dynamic position " +
<xsl:value-of select="position()" /> +
" IsDefault: " +
( <xsl:value-of select=
"/*/MainProducts/MainProduct
[position()=$vPos]/Published" />
== 1 ? "true" : "false" ));
</script>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root>
<MainProducts>
<MainProduct>
<Published>0</Published>
</MainProduct>
<MainProduct>
<Published>1</Published>
</MainProduct>
</MainProducts>
<SubProducts>
<SubProduct>
<IsDefault>1</IsDefault>
</SubProduct>
<SubProduct>
<IsDefault>0</IsDefault>
</SubProduct>
</SubProducts>
</root>
the wanted, correct result is produced:
<script type="text/javascript">
console.log("Dynamic position " +
1 +
" IsDefault: " +
( 0
== 1 ? "true" : "false" ));
</script>
<script type="text/javascript">
console.log("Dynamic position " +
2 +
" IsDefault: " +
( 1
== 1 ? "true" : "false" ));
</script>
Problem
I realize this code isn't the cleanest, but properly refactoring it unfortunately isn't an option. The issue is that I would expect `position()` on the second iteration to return a true value. But when using `position()` it never returns true, as expected on the second iteration. But if I hard code the selection values, it returns the expected result. Here is an example: ``` <root> <MainProducts> <MainProduct> <Published>0</Published> </MainProduct> <MainProduct> <Published>1</Published> </MainProduct> </MainProducts> <SubProducts> <SubProduct> <IsDefault>1</IsDefault> </SubProduct> <SubProduct> <IsDefault>0</IsDefault> </SubProduct> </SubProducts> </root> ``` XML content: ``` <xsl:for-each select="/root/SubProducts/SubProduct"> <script type="text/javascript"> // Always returns false console.log("Dynamic position " + <xsl:value-of select="position()" /> + " IsDefault: " + ( <xsl:value-of select="/root/MainProducts/MainProduct[position()]/Published" /> == 1 ? "true" : "false" )); </script> </xsl:for-each> <script type="text/javascript"> // Returns false, but expected to return true on second iteration. console.log("Hard coded position 1 IsDefault: " + ( <xsl:value-of select="/root/MainProducts/MainProduct[1]/Published" /> == 1 ? "true" : "false" )); // Returns true console.log("Hard coded position 2 IsDefault: " + ( <xsl:value-of select="/root/MainProducts/MainProduct[2]/Published" /> == 1 ? "true" : "false" )); </script> ``` Here is the exact console output: ``` Dynamic position 1 IsDefault: false Dynamic position 2 IsDefault: false Hard coded position 1 IsDefault: false Hard coded position 2 IsDefault: true ``` What am I missing here that `position()` isn't selecting the node properly?