Reputation: 1522
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
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
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