Spencer
Spencer

Reputation: 1522

xslt passing position as a parameter is producing strange results?

I have an xml file which looks like this:

<section>
  <title> title of section </title>
  <table>
     <title> title of table</title>
     <tableData columns = 2>
     <column width ="50"/>
     <column width ="100"/>
     <tableHead>
        <row>
             <entry>column 1</entry>
        </row> 
        <row>
             <entry>column 2</entry>
        </row>
     </tableHead>
     <tableBody>
       <row>
             <entry>text</entry>
             <entry>abc</entry>
       </row> 
       <row>
             <entry>text</entry>
             <entry>dbe</entry>
      </row> 
       <row>
             <entry>text</entry>
             <entry>fgh</entry>
       </row> 
     </tableBody>
   </table>
 </section>

I need to process each entry in tableHead/row/entry and spit it out in some format that also includes the columnWidth. So, my output must look like {"name":"column 1", "width":"50px"},{"name":"column 2", "width":"100px"}

My transform looks like this

 <xsl:template match="//table/tableHead/row/entry">
    <xsl:text>{"name": "</xsl:text><xsl:value-of select"."/>
    <xsl:text>", "width":"</xsl:text>

    <xsl:call-template name="get_column_width">
       <xsl:with-param name ="rowNum">
          <xsl:value-of sellect"position()"/>
       </xsl:with-param>
    </xsl:call-template>
  </xsl:template>


 <xsl:template name ="get_column_width">
     <xsl:param-name="rowNum">
     <xsl:value-of select="../../../columnWidth[$rowNum]/@width"/>
     <xsl:text>px"}</xsl:text>
 </xsl:template>

Strangely, this produces {"name":"column 1", "width":"50 100px"},{"name":"column 2", "width":"50 100px"}

However, when I output only the rowNum parameter I get exactly what I expect:

{"name":"column 1", "width":1px"},{"name":"column 2", "width":"2px"}

So, rowNum is 1 and 2 so the select statement will use columnWidth[1] and columnWidth[2], but for whatever reason it just outputs both of them. I tried using columnWidth[1] in the select and it outputs 50 as I would expect. columnWidth[2] outputs 100. I'm really confused here. Using preceding-sibling gave the same result. Is there something wrong with my template matching?

Unfortunately changing the xml format is not an option.

Upvotes: 2

Views: 2077

Answers (2)

Michael Kay
Michael Kay

Reputation: 163625

The value of $rowNum is a result tree fragment (or in XSLT 2.0, a document node). The effective boolean value of a result tree fragment (or a node) is always true. You probably imagined that the value was an integer. But to set a variable to an integer, you should write

<xsl:with-param name="x" select="position()"/>

not

<xsl:with-param name="x">
  <xsl:value-of select="position()"/>
</xsl:with-param>

Sadly the latter construct seems to be incredibly prevalent, considering how verbose and inefficient it is, and how occasionally (as here) it gives the wrong answer.

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243599

Instead of:

<xsl:value-of select="../../../columnWidth[$rowNum]/@width"/>

Use:

<xsl:value-of select="../../../columnWidth[position() = $rowNum]/@width"/>

Explanation

In XPath:

someExpression[someInteger]

is a shorthand for:

someExpression[position() = someInteger]

However, in case of:

someExpression[$someVariableReference]

the XPath 1.0 processor typically doesn't know what value and of what type is contained in the variable. Therefore, it converts this variable to a boolean value (true() or false()) to perform the complete evaluation.

By definition:

boolean(someNumber)

is equivalent to:

not(somenumber = 0)

As neither of the numbers 1 and 2 is zero, the predicate [$rowNum] is true() in both cases,

Thus:

../../../columnWidth[$rowNum]/@width

selects the width attributes of both columnWidth elements.

Upvotes: 2

Related Questions