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?

Original source