I have a stylesheet that I developed in version 1.0. I needed to convert to version 2.0 to take advantage of some additional features. Now however, when I use the following syntax I get all the results instead of just the first one. This worked in v1.0 but does not work in v2.0:
//elementName[1]
Is there a simple fix?
That XPath will return the same nodes in both versions (namely all the elementName elements in the document that are the first child with that name in their respective parent elements), but
<xsl:value-of select="//elementName[1]"/>
will give different results. In XSLT 1.0 the behaviour of value-of when given a set of nodes is to output the value of the first node in the set in document order and ignore the others, but in 2.0 it will output the values of all of them, separated by spaces. If you want to restrict to the first item in the sequence you should do so explicitly with (....)[1].
Yes, the fix is simple...
(//elementName)[1]
This will give you the first occurrence. Your previous xpath was every elementName that was the first elementName child of its parent.
A good example from the spec:
NOTE: The location path //para[1] does not mean the same as the
location path /descendant::para[1]. The latter selects the first
descendant para element; the former selects all descendant para
elements that are the first para children of their parents.
Related
I have a xslt transformation from xml to pdf using apache fop. Whether is possible to add codnition to xslt transormation? I want to not display column in result pdf if all values in column equals zero. It's possible?
Yes, it is possible. In fact, you can do declarative/functional programming logic with XSLT using templates, functions and conditionals. Since you did not provide any example code, I will suppose that you have a table element in your xslt, and you want to show it only if there is some non-zero value to be displayed.
In this case, you should use an recursive template to walk along the list in your xml, and keep checking with XPath if the values are equal to zero. If you find zero, call this same template for the next value, if not, call the template that builds the table for you.
Notice that to use condition, you can use xsl:for-each, xsl:when or xsl:if elements to make the decision given the XPath expression. The xsl:template is used for iterating recursively with parameters, given that instance variables do not exist in xsl.
I would like to go through the whole xml document that I have, without depending in the actual id value, node name or attributes.
I use the msxml3 lib.
I would like to get a list of the main nodes in the xml, that are descendants of the main node.
<mainNode>
<firstNodeInList></firstNodeInList>
<secondNodeInList></secondNodeInList>
<thirdNodeInList></thirdNodeInList>
</mainNode>
I would like to get a list of the inside nodes, i.e. :
firstNodeInList->secondNodeInList->thirdNodeInList.
Thank you
Since no one responded, I had to find out the answer, which apperently is very simple.
The first line will get the document element, or the root element. The second will get the list of children of the root.
MSXML2::IXMLDOMElementPtr docElem = m_newFileDoc->documentElement;
MSXML2::IXMLDOMNodeListPtr nodes = docElem->childNodes;
The xsl copy function appears to deviate from the functional hierarchical pattern of the copy-of in that it doesn't assess the select condition (if condition is an XPath axes) before applying the function action.
so xsl:copy select="ancestor::*" will behave different than xsl:copy-of select="ancestor::*"
At least in XSLT 1.0, xsl:copy does not have a select attribute: it only copies the current node.
From the spec (emphasis mine):
The xsl:copy element provides an easy way of copying the current node. Instantiating the xsl:copy element creates a copy of the current node. The namespace nodes of the current node are automatically copied as well, but the attributes and children of the node are not automatically copied. The content of the xsl:copy element is a template for the attributes and children of the created node; the content is instantiated only for nodes of types that can have attributes or children (i.e. root nodes and element nodes).
Edit: XSLT2.0 xsl:copy behaves the same way
Well, copy and copy-of are intended to do very different things. copy always works on the current node and does not support the select attribute. See http://zvon.org/xxl/XSLTreference/Output/xslt_copy.html vs http://zvon.org/xxl/XSLTreference/Output/xslt_copy-of.html.
A select attribute is being added to xsl:copy in XSLT 3.0, to allow copying of nodes other than the context node (useful for example in a function). For XSLT 1.0 and 2.0, I really don't know what you mean.
I have done a search for all nodes that have an attribute containing (substring) a String. These nodes can be found at different levels of the tree, sometimes 5 or 6 levels deep. I'd like to know what parent/ancestor node they correspond to at a specified level, 2 levels deep. The result for the search only should be much greater than the results for the corresponding parents.
EDIT to include code:
/xs:schema/xs:element/descendant::node()/#*[starts-with(., 'my-search-string-here')]
EDIT to clarify my intent:
When I execute the Xpath above sometimes the results are
/xs:schema/xs:element/xs:complexType/xs:attribute or
/xs:schema/xs:element/xs:complexType/xs:sequence/xs:element or
/xs:schema/xs:element/xs:complexType/xs:complexContent/xs:extension/xs:sequence/xs:element
These results indicate a place in the Schema where I have added application specific code. However, I need to remove this code now. I'm building an "adapter" schema that will redefine the original Schema (untouched) and import my schema. The String I am searching for is my prefix. What I need is the #name of the /xs:schema/node() in which the prefix is found, so I can create a new schema defining these elements. They will be imported into the adapter and redefine another schema (that I'm not supposed to modify).
To reiterate, I need to search all the attributes (descendants of /xs:schema/xs:element) for a prefix, and then get the corresponding /xs:schema/xs:element/#name for each of the matches to the search.
To reiterate, I need to search all the attributes (descendants of /xs:schema/xs:element) for a prefix, and then get the corresponding /xs:schema/xs:element/#name for each of the matches to the search.
/
xs:schema/
xs:element
[descendant::*/#*[starts-with(., 'my-search-string-here')]]/
#name
This should do it:
/xs:schema/xs:element[starts-with(descendant::node()/#*, 'my-search-string-here')]
You want to think of it as
select the xs:elements which contain a node with a matching attribute
rather than
select the matching attributes of descendant nodes of xs:elements, then work back up
As Eric mentioned, I need to change my thought process to select the xs:elements which contain a node with a matching attribute rather than select the matching attributes of descendant nodes of xs:elements, then work back up. This is critical. However, the code sample he posted to select the attributes does not work, we need to use another solution.
Here is the code that works to select an element that contains and attribute containing* (substring) a string.
/xs:schema/child::node()[descendant::node()/#*[starts-with(., 'my-prefix-here')]]
With XSLT, I am processing an xml file which might not have any nodes, other than the root. I want to output a special message on the html page for this case. I am using a for-each element to process the xml file. How do I check if the xml file has any actual nodes in it?
How Do I Check if an XML File Has No
Nodes?
By definition, any well-formed XML document contains at least a top element.
Therefore, any XML document contains some nodes -- there can't be an XML document that has no nodes.
I understand this question as asking: "How to determine that the top element of an XML document has no descendents?"
This is in fact an XPath question. This XPath expression:
/*[not(node())]
is true() exactly when the top element of the document has no child nodes (elements, text-nodes, processing instructions or comments).
The top element can still have attributes and it always has namespace nodes, but these two kinds of nodes are not considered to be exactly "children".
/*[not(node()) and not(#*)]
is true() exactly when the top element has no child nodes and no attributes.
/*[not(*)]
is true() exactly when the top element has no child element nodes (but it still can have text-node children, processing-instruction children and comment-nodes children).