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,
//
Related
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 select trailing empty nodes? Empty, as in no text, that is.
In this example I want to ignore the first empty node, as it is not trailing, The last three nodes (under <bar>) have no text so I want to select them.
<foo>
<bar>
<node>blah blah</node>
<node></node> <-- Not this
<node>blah blah</node>
<node>blah blah</node>
<node></node> <-- But this
<node><node></node></node> <-- and this
</bar>
</foo>
If you use /foo/bar/node[not(normalize-space()) and not(following-sibling::node[normalize-space()])] you select the two node child elements that don't have any following sibling with text content. The second of those node elements contains a further node child, I am not sure whether you want to select that as well as part of the result.
I have a large XSD I process using several templates to get a new XSD.
In one of the last steps I would like to determine the length of the xml (actually an XSD) that was captured in a variable xsdresult.
Using the string-length function I see a strange length not matching the variable length of xsdresult. Size of string/xsd is over 52000 chars but I see Length: 9862 What am I doing wrong?
<!-- Catch output in variable -->
<xsl:variable name="xsdresult">
<xsl:call-template name="start"/>
</xsl:variable>
<xsl:template name="start">
<xsl:apply-templates/>
</xsl:template>
<!-- Build required doc parts -->
<xsl:variable name="docparts">
<xsl:call-template name="builddocparts"/>
</xsl:variable>
<xsl:template name="builddocparts">
Length: <xsl:value-of select="string-length(normalize-unicode($xsdresult))"/>
</xsl:template>
...
A call to string-length() is equivalent to a call to string-length(.), which in turn coerces the current node to a string, so it's equivalent to string-length(string(.)). The value of the string() function is the string value of the node, which for an element node is the string formed by the concatenation of all descendant text nodes.
If you want to know how the minimum amount of space the serialized XML document will take on disk, given a simple serialization, then you must add:
For each non-empty element, the length of its start-tag: the length of the element type name, plus 2 for the start-tag delimiters < ... >, plus the sum of the lengths of the attribute-value specifications.
For each attribute-value specification, you will need one character for leading whitespace, plus the length of the attribute name, plus the string length of the attribute's value, plus three for the equal sign and quotation marks, plus five characters for each time a quotation mark is replaced by ' or ".
For each non-empty element, the length of its end-tag (length of its element type name plus 3).
For each empty element, the length of its sole tag (length of its element type name, plus length of its attribute-value specifications, plus 3).
For each occurrence of < in the data or in attribute values, three characters for the escaping as <.
For each occurrence of ampersand in the data or in attribute values, four characters for escaping as &.
Not part of the minimum amount, but possibly part of the space you'll need on disk:
The total width of any whitespace added, if you indent the XML structurally.
The number of CDATA marked sections you serialize, times 12 (for <![CDATA[ + ]]>).
The number of characters saved by using CDATA marked sections instead of < and &.
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).
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.