XSLT: xpath to get parent element on matched child element name - xslt

I am new to xslt.
I want to get the parent node of matched child node(ends with)
Sample XML:
can have elements before and after parent tag
Here the result should be "parent"
Query: find first parent element name of the child element ends with smile ("endwithsmile")
I could able to get the element which contains ("endswithsmile"), but not ends with .
ends-with is not working.
<xsl:value-of select="(//*[contains(local-name(), 'Identifier')])"/>
I also tried using "../" to get parent but didnt work.
Sample xml for above question :::
<parent>
<childendwithsmile>
<test></test>
</childendwithsmile>
</parent>
<parent1>
<childendwithsmile>
<test></test>
</childendwithsmile>
</parent1>

Try this XPath:
//*[substring(name(), string-length(name()) - string-length('smile') + 1) = 'smile']/..

#Kiril's answer should work, but I prefer this approach that selects a parent based on its child, rather than selecting the child and going back up the tree:
//*[*[substring(name(), string-length(name()) - string-length('smile') + 1) = 'smile']]

Related

return any node with child nodes name matching multiple

I want to retrieve any Node with the child nodes childTypeA, childTypeB or childTypeC, but not return nodes with only other child nodes (like stepChildA).
Once I have that node, I can retrieve any of the child nodes and their attributes. But I can't figure how to filter out those nodes that do not have any child nodes matching childTypeA, childTypeB or childTypeC.
My efforts either return all nodes with children, or return a node for each matching child, which means the same node is returned one, two or three times, depending upon if there is one, two or all three of the desired child nodes.
With xml data as shown
<parent Name="Item one">
<OtherData Name="Data one">
<childTypeA>
<someData Name="Child A">
</childTypeA>
<childTypeB>
<someData Name="Child B">
</childTypeB>
</parent>
<parent Name="Item two">
<OtherData Name="Data two">
<childTypeB>
<someData Name="Child B">
</childTypeB>
<childTypeC>
<someData Name="Child C">
</childTypeC>
</parent>
<parent Name="Item three">
<OtherData Name="Data three">
<stepChildA>
<someData Name="Step Child A">
</stepChildA>
</parent>
The actual data under each child type is different and I'm trying to assemble it into a table, where each parent node with the desired child type appear on a single row and the child data align under the appropriate columns. Currently I've either had all parent nodes where the data desired appears as intended, but also includes rows with the other parent nodes which have no data, or, I am getting multiple rows when there is more than one of the desired child type. The specific child type data fall into the proper columns, but are not on one row.
My approach was correct, but I needed to re-order my code. I had the "if test" before the "for-each". By swapping them, I was able to return all parent nodes, but then use the "xsl:if test=..." to ignore the unwanted parents and build each table row as I parse through the child nodes. Note I added a sort to the returned parent nodes, based upon the value of their attribute #Name.
<xsl:for-each select="parent">
<xsl:sort select="#Name">
<xsl:if test="childTypeA or childTypeB or childTypeC">
<tr>.........</tr>
</xsl:if>
</xsl:for-each>
My XML data is very verbose and I am trying not to overwhelm the post. I am also struggling with the formatting rules.
As far as I can tell, you simply need to use the xpath parent[childTypeA | childTypeB | childTypeC] like this:
<xsl:for-each select="parent[childTypeA | childTypeB | childTypeC]">
<xsl:sort..etc.
</xsl:for-each>

Xpath - Select everything that is not the first p

I am trying to select every node in an XHTML file that is not the first P tag.
I'm modifying an existing stylesheet (h2d.xsl, for those who might look for it later) and currently the expression is:
<xsl:apply-templates select="(body/*|body/text()|body/comment())[1]" mode="creating-content-before-section"/>
And essentially I'd like to modify to skip p[1]. I tried things like
<xsl:apply-templates select="(body/*[not(self::p[1])]|body/text()|body/comment())[1]" mode="creating-content-before-section"/>
But that does not work...
Any idea XSLT gurus?
The following XPath should return any child of body element except the first p child element :
body/*[not(self::p and count(preceding-sibling::p)=0)]

Does node-set() have an ancestor node?

I feel, this is a basic XSLT question. From how I understand, a node-set is data structure in itself. From a node-set() I cannot navigate to any node that is outside of the set. i.e I cannot reach the parent, the beginning of the xml nor its siblings. Is this right?
or
Is there a way to get a parent of a node-set()
Code
<neighbourhood>
<parent name = "xyz">
<child address=10> a </child>
<child address=10> b </child>
<child address=15> c </child>
</parent>
</neighbourhood>
I have a set of child nodes. I need to eliminate nodes with duplicate 'address'.
There can be n number of 'parent' and m number of 'child' and there could be a grandparent node for the 'child'.
The best and the logical way is probably go by each parent and process their children. But, it is an existing code base and this is an oversimplified example. I do not want to break too many things by touching the caller function and other templates.
So, my question was if I could get the 'parent' with the set of 'child' nodes I have
Thanks for all your responses
Does node-set() have an ancestor node?
node-set() is a (extension) function -- not a node. A function cannot have ancestor, because it is not a node.
I suppose that by "node-set()" in the question, you mean the value that is returned by the xxx:node-set() function (where the prefix "xxx" is bound to a vendor-specific namespace). If so, here is the wanted answer:
By definition, the xxx:node-set() function returns the document-node() (also known as root-node in XPath 1.0) of a temporary tree, which is obtained by converting the RTF (Result Tree Fragment), passed as the only argument to this function.
A document-node by definition is at the top of the document hierarchy and is the only node in an XML document, that doesn't have a parent.
Therefore, the node returned by a called xxx:node-set() function doesn't have any ancestors.
From a node-set() I cannot navigate to any node that is outside of the set. i.e I cannot reach the parent, the beginning of the xml nor its siblings. Is this right?
Yes, without calling another function that returns a node from another document (such as the standard XPath function id() or the standard XSLT function document()), or referencing a variable/parameter, it is not possible to navigate to a node from another document only by using XPath location steps.
or
Is there a way to get a parent of a node-set()
No, the node returned by the xxx:node-set() function is a document node and a document node doesn't have a parent (or any other ancestor) node.
Don't mix up node sets and node-set()s.
What do I mean by this? Well, a node set is a set of nodes. In normal, unextended XSLT 1.0, this means a selection of nodes from your input document. If I do this:
<!-- a node set -->
<xsl:variable name="my-node-set"
select="/indoc/level1/level2"/>
the variable $my-node-set contains a set of level2 nodes, but those nodes still live in the input document. If I subsequently do a for-each like so:
<nodeset-from-indoc>
<xsl:for-each select="$my-node-set/level3">
<parent>
<xsl:value-of select="local-name(..)"/>
</parent>
<grandparent>
<xsl:value-of select="local-name(../..)"/>
</grandparent>
</xsl:for-each>
</nodeset-from-indoc>
I will get the names of the parents and grandparents of each node:
<nodeset-from-indoc>
<parent>level2</parent><grandparent>level1</grandparent>
<parent>level2</parent><grandparent>level1</grandparent>
<parent>level2</parent><grandparent>level1</grandparent>
</nodeset-from-indoc>
If, however, I hard-code nodes into a variable:
<!-- a result-tree fragment -->
<xsl:variable name="my-rtf">
<level2>
<level3>1</level3>
</level2>
<level2>
<level3>2</level3>
</level2>
<level2>
<level3>3</level3>
</level2>
</xsl:variable>
this is not a node set, but a result tree fragment, since they weren't selected from the input document. The problem with result tree fragments is that you can't use XPath on them. I can't, for example, do this:
<xsl:for-each select="$my-rtf/level3">
This is where the node-set() function comes in. It is an extension to XSLT 1.0, which comes from some extension namespace, depending on your XSLT processor. Many processors choose to implement this in the namespace defined by EXSLT.
As Dmitre points out, the node-set() function returns a magic document node of a temporary tree, allowing you to use XPath. However, this causes a subtle shift in how the select needs to be done. Because of the magic document node, I have to include level2 in my selection:
<nodeset-from-rtf>
<xsl:for-each select="exsl:node-set($my-rtf)/level2/level3">
<parent>
<xsl:value-of select="local-name(..)"/>
</parent>
<grandparent>
<xsl:value-of select="local-name(../..)"/>
</grandparent>
</xsl:for-each>
</nodeset-from-rtf>
And in this case, the level3 nodes will have parents, but no grandparents:
<nodeset-from-rtf>
<parent>level2</parent><grandparent/>
<parent>level2</parent><grandparent/>
<parent>level2</parent><grandparent/>
</nodeset-from-rtf>
A node-set is a set of nodes. Each node in the node-set has ancestors. The node-set itself does not. If $NS is a node-set, you can do $NS/ancestor::node(): this will give you all the ancestors of all the nodes in the node-set, with duplicates eliminated.

xsl - select node by child

I have a problem selecting just elements of an xml, which contain a specific child node. Asumme the following part of an xml:
<root>
<Navision.Buchungen>
<Saldo>-110867.7500</Saldo>
<Navision.Kontostruktur>
<Bereich>1</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
<Navision.Buchungen>
<Saldo>-3082585.2100</Saldo>
<Navision.Kontostruktur>
<Bereich>2</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
...
</root>
Now I have an xsl part like this to get the sum of 'Saldo':
<xsl:variable name="FACT0" select="sum(//root/Navision.Buchungen/Saldo)"/>
But how can I select just the Saldo for 'Bereich' 1 for example?
Use this XPath:
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
Edited:
oh already posted.
For further problems you can use one of the online testbeds like this one. And of course good manuals like those from w3schools, also with testbeds for xsl

How to remove a child node from an XML file in C++ using Xerces-C?

root = doc->getDocumentElement();
child=root->getLastChild();
DOMNode* removedElement = root->removeChild(child);
removedElement->release();
The child is getting newline character as a node if the XML file is like this:
<root>
<child1> </child1>
<child2> text </child2>
</root>
The same code is working fine and removing child if the XML file is of the format
<root> <child1></child1><child2>text</child2> </root>
How can I get rid of it (the newline)?
Found the answer myself.
The understanding of DOM is different. The children of <root> in this case are the text nodes of root, child1, text node of child1, child2, text node of child2.
So the number of children of root is 5. But generally, as per XML notations, we thought they are 2.
So here when I try to remove the last child it is an error. We can remove that text node from child2.