XSL:Number count not working as expected -- Issue with my XPath? - xslt

Here's the XML
<row>
<cell>blah blah</cell>
<check>
<option/>
<option/>
<option/>
<option/>
<option/>
<option/>
</check>
</row>
Here is the XSL
<xsl:template match="row">
<xsl:variable name="inputLevel">
<xsl:number count="option" level="any" from="."/>
</xsl:variable>
<xsl:value-of select="$inputLevel"/>
</xsl:template>
All I get is "0". http://www.w3schools.com/XPath/xpath_syntax.asp says "." means the current node. Shouldn't it be returning "6"?
Edit1: I wanted to look for option tags at ANY level, not just check. Should have explained but the option tags could exist at any level below

If you want to count descendant options you shouldn't use xsl:number but:
<xsl:variable name="inputLevel" select="count(.//option)">

I think the problem is that the xpath expression option won't match anything at the row element - try this instead:
<xsl:number count="check/option" level="any" from="." />
To look for option elements at any level use the following syntax:
<xsl:number count="//option" level="any" from="." />
I don't think the from attribute is reqired, and the level attribute probably isn't doing what you think it is (I'm also not sure what it does...)

From the XSLT 1.0 W3C specification:
"If no value attribute is specified, then the xsl:number
element inserts a number based on the
position of the current node in the
source tree. The following
attributes control how the current
node is to be numbered:
The levelattribute specifies what
levels of the source tree should be
considered; it has the values
single, multiple or any. The
default is single.
The count attribute is a pattern
that specifies what nodes should be
counted at those levels. If count
attribute is not specified, then it
defaults to the pattern that matches
any node with the same node type as
the current node and, if the current
node has an expanded-name, with the
same expanded-name as the current node
When level="any", it constructs a
list of length one containing the
number of nodes that match the count
pattern and belong to the set
containing the current node and all
nodes at any level of the document
that are before the current node in
document order, excluding any
namespace and attribute nodes (in
other words the union of the members
of the preceding and ancestor-or-self
axes). If the from attribute is
specified, then only nodes after the
first node before the current node
that match the from pattern are
considered. ".
From this text it is clear that only nodes that are ancestors or are preceding the current node are counted.
In this question, the current node is the top element node row and it has 0 ancestor and 0 preceding element nodes.
Therefore, the returned result is correct!
Solution:
Use:
count(descendant::option)
The result of evaluating this expression is the count of all option elements in the document, that are descendents of the current node (the row element).

Related

how to count the value of multiple elements in xslt

I have this xslt code to count the abstract length of type main:
<xsl:variable name="mainAbstract">
<xsl:value-of select="string-length(normalize-space(abstract[#type = 'main']))"/>
</xsl:variable>
and I had an issue where I had multiple xml tags that matches the same pattern and it's not working.
How should I count the number of characters in multiple elements from this select statement?
If you have multiple abstract elements of type main, then you need to select which one you want to process.
If - as it seems - you want the sum of their individual string-lenghts, then do:
<xsl:value-of select="sum(abstract[#type = 'main']/string-length(normalize-space()))"/>

How do I test whether a text node intervenes between the current node and a defined preceding sibling?

In XPath/XSLT 2.0, how do I test whether a text node intervenes between the current node and a preceding sibling <lb/>, regardless whether non-text nodes intervene?
XML:
<lb/>I'd like to test whether this text exists.<someNode/><currentNode/>
Bonus question: what is the desired syntax if the <lb/> may be either a sibling of the current node or of the parent node?
<xsl:if test="preceding-sibling::text() >> preceding-sibling::lb"> tests whether there is a preceding sibling text node after a preceding sibling lb element: http://xsltransform.net/6pS1zCK.
In general (if there can be more than one element or text node) I think you need to use some $sib in preceding-sibling::lb satisfies some $text in preceding-sibling::text() satisfies $sib >> $text.

Meaning of this XPath expression

I am new to XML and XSLT programming.
Can anybody explain the meaning of below XPath expression?
<xsl:apply-templates select="//Order[Header/string-length(ORDERID) > 0]/Header/SAP_WBSELEMENT[not(. = following::SAP_WBSELEMENT)]" />
Meaning: Select SAP_WBSELEMENT elements, including those with duplicate string values only once, that are children of Header elements that are children of any Order elements in the document with a Header child with an ORDERID with an non-empty string value.
Breakdown: Working from the end of the XPath back to the front...
Select SAP_WBSELEMENT elements, excluding those with duplicate string values,
SAP_WBSELEMENT[not(. = following::SAP_WBSELEMENT)]
that are children of Header elements,
Header/
that are children of those Order elements with a Header child with an ORDERID with an non-empty string value,
Order[Header/string-length(ORDERID) > 0]/
anywhere in the document,
//

Copy an element and truncate it with xslt

I am trying to select all the table cells in a particular column and truncate them down to 10 characters.
I really can't fault this syntax.
<xsl:template match="table[contains(#class, 'listing')]//tr/td[contains(#class, 'listing-body-start')]">
<td>
<xsl:value-of select="substring(./text(), 1, 10)"/>
</td>
</xsl:template>
I can replace the contents of the cell with static text, or replicate the same text by using select=".", but as soon as I try this substring method, the cells dissappear.
Try to avoid using text(). It's generally bad practice.
Use
<xsl:value-of select="substring(., 1, 10)"/>
This works on the string value of the td element, rather than on its child text nodes. In many content models it would be normal for a td element to contain other elements as children, e.g. a p or para element, and in that case looking for the child text nodes will fail.
This answer is a guess. If you don't tell us what's in your input, we have no choice but to guess.

XPath, XSL: How to select all nodes, not just elements, when xsl:param is a node-set?

In an XSL recursion, this
<xsl:with-param name="items" select="$items[position() > 1]
would normally work to recurse with all nodes but the first. But it only works if the nodes are elements. If they are a mix of element and text nodes, the recursion fails, since, for example, $items[3] returns the third element node, not the third node, regardless whether text or element.
So the expression sets the new $items to just the element nodes in the old $items. The text nodes are lost.