XPath Find any element with text() within node selection - xpath

I have a fairly deeply nested xml structure and I would like to find an element with a particular value after I have already selected a node. In the sample below I have an array of 'B' and after selecting each of the 'B' nodes I would like to get the text of one of the children (which are not consistent) that starts with the word 'history'
<A>
<Items>
<B>
<C>
<D>history: pressed K,E</D> // could be nested anywhere
</C>
</B>
<B>
<C>
<E>history: pressed W</E>
</C>
</B>
</Items>
</A>
// Select the nodes from the array (B)
var nodes = select(xmldoc, "//A/Items/B");
// Iterate through the nodes.
nodes.forEach(node){
// is it possible to select any element that starts with the text 'history' from the already selected node.
var history = select(node, "???[starts-with(.,'history')]");
all the samples I have seen start with : //*[text()] which searches from the root of the structure.

//B//*[starts-with(normalize-space(), 'history')]
looks like it would do what you intend.
It selects "any descendant element of <B> whose text content starts with 'history'".
Manual iteration to find further nodes is not typically necessary. XPath does that for you. If you must iterate for some other reason, use the context node . to select.
nodes.forEach(function (node) {
var history = select(node, "./*[starts-with(.,'history')]");
});
If you are actually looking for "any text node..."
//B//text()[starts-with(normalize-space(), 'history')]
Or "any element node that has no further child elements..."
//B//*[not(*) and starts-with(normalize-space(), 'history')]

Related

XPath : check if first child is desired or not

I want to check if the first child is B or not in the following code:
<A>
<B>
<C> 123</C>
</B>
</A>
<A>
<E>
<C> 00</C>
</E>
<B>
<C>121</C>
</B>
</A>
Here there are two A and in both we have B. I want to check if first child of A is B and print value in C in it.
How can I do this. using xpath?
How about:
//A/*[1][self::B]/C
It gets the first child element of A node that is a B node, then gets its C node content.
Use this XPath-1.0 expression:
A[child::*[1] = child::B]/B[1]/C
It checks if the first child node of A is identical with the first B child node. If yes, then it selects the first B node and returns the value of its C child.

XPath with specific following sibling case

I have structure that looks something like this
<p>
<br>
<b>Text to fetch </b>
<br>
"Some random text"
<b>Text not to fetch</b>
I need XPath that will allow me to fetch following sibling of the br element only if there is no text between br element and his following sibling.
If I do something like this
//br/following-sibling::b/text()[1]
It will fetch both Text to fetch and Text not to fetch, while I only need Text to fetch.
Another possible XPath :
//br/following-sibling::node()[normalize-space()][1][self::b]/text()
brief explanation:
//br/following-sibling::node(): find all nodes that is following-sibling of br element, where the nodes are..
[normalize-space()]: not empty (whitespace only), then..
[1]: for each br found, take only the first of such node, then..
[self::b]: check if the node is a b element, then if it is a b element..
/text(): return text node that is child of the b element
Try below XPath to avoid matching b nodes with preceding sibling text:
//br/following-sibling::b[not(preceding-sibling::text()[1][normalize-space()])]/text()

XPath difference between two similar path and other questions

I've to made some exercices but
I don't really understand the difference between two similar path
I've the tree :
<b>
<t></t>
<a>
<n></n>
<p></p>
<p></p>
</a>
<a>
<n></n>
<p></p>
</a>
<a></a>
</b>
And we expect that each final tag contain one text node.
I've to explain the difference between //a//text() and //a/text()
I see that //a//text() return all text nodes and it seems legit,
but why //a/text() return the last "a node" -> text node ?
Another question :
why //p[1] return for each "a node", the first "p" child node ?
-> I've two results
<b>
<t></t>
<a>
<n></n>
**<p></p>**
<p></p>
</a>
<a>
<n></n>
**<p></p>**
</a>
<a></a>
</b>
Why the answer is not the first "p" node for the whole document ?
Thanks for all !
Difference between 1: //a//text() and 2: //a/text()
Let's break it down: //a selects all a elements, no matter where they are in the document. Suppose you have /a, that would select all root a elements.
If the / path expression comes after another element in an XPath expression, it will select elements directly descending the element before that in the XPath expression (ie child elements).
If the // path expression comes after another element in an XPath expression, it will select all elements that are descendant of the previous element, no matter where they are under the previous element.
Applying to your two XPath expressions:
//a//text(): Select all a elements no matter where they are in the document, and for those elements select text() no matter where they are under the a elements selected.
//a/text(): Select all a elements no matter where they are in the document, and for those elements select any direct descendant text().
Why //p[1] returns for each "a node", the first "p" child node?
Suppose you were to write //a/p[1], this would select the first p child element of any a element anywhere in the document. By writing //p[1] you are omitting an explicit parent element, but the predicate still selects the first child element of any parent the p element has.
In this case there are two parent a elements, for which the first p child element is selected.
It would be good to search for a good introduction to XPath on your favorite search engine. I've always found this one from w3schools.com to be a good one.

Xpath to select only nodes where child has certain value

Hope someone can help
<root>
<a>
<b>1.00</b>
<c>
<d/>
</c>
</a>
<a>
<b/>
</a>
</root>
How can I formulate an XPATH expression in which I get b's value only if the parent node has a D node
i.e., in the previous example I would get only the value of the first b, as the second a node doesn't have a /c/d
Try //*[.//d]/b
i.e. any b node, child of any node -- * -- that has a d descendant
You need to use a predicate to filter
/root/a[c/d]/b
i.e. starting with the root element, find all its a children that have a c/d, and finally extract the b children of these filtered a elements.

XPath: limit scope of result set

Given the XML
<a>
<c>
<b id="1" value="noob"/>
</c>
<b id="2" value="tube"/>
<a>
<c>
<b id="3" value="foo"/>
</c>
<b id="4" value="goo"/>
<b id="5" value="noob"/>
<a>
<b id="6" value="near"/>
<b id="7" value="bar"/>
</a>
</a>
</a>
and the Xpath 1.0 query
//b[#id=2]/ancestor::a[1]//b[#value="noob"]
The Xpath above returns both node ids 1 and 5. The goal is to limit the result to just node id=1 since it is the only #value="noob" element that is a descendant of the same <a> that (//b[#id=2]) is also a descendant of.
In other words, "Find all b elements who's value is "noob" that are descendants of the a element which also has a descendant whose id is 2, but is not the descendant of any other a element". How's that for convoluted? In practice the id number and values would be variable and there would hundreds of node types.
If the id=2, we would expect to return element id=1 not id=5 since it is contained in another a element. If the id=4, we would expect to return id=5, but not id=1 since it is not in the first ancestor a element as id=4.
Edit:
Based on the comments of Dimitre and Alejandro, I found this helpful blog entry explaining the use of count() with the | union operator as well as some other excellent tips.
Use:
//b[#value='noob']
[count(ancestor::a[1] | //b[#id=2]/ancestor::a[1]) = 1]
Explanation:
The second predicate assures that both b elements have the same nearest ancestor a.
Remember: In XPath 1.0 the test for node identity is:
count($n1 | $n2) = 1
First, this
is there some way to limit the result
set to the <b> elements that are ONLY
the children of the immediate <a>
element of the start node
(//b[#id=2])?
//b[#value='noob'][ancestor::a[1]/b/#id=2]
It's not the same as:
Starting at a node whose id is equal
to 2, find all the elements whose
value is "noob" that are descendants
of the immediate parent c element
without passing through another c
element
Wich is:
//c[b/#id=2]//*[.='noob'][ancestor::c[1][b/#id=2]]
Besides these expressions, when you are dealing with "context marks" you can use the set's membership test as in:
$node[count(.|$node-set)=count($node-set)]
I leave you its use for this case as an exercise...
//b[#id=2]/ancestor::a[1]//b[#value="noob" and not(ancestor::a[2]=//b[#id=2]/ancestor::a[1])] ?
that works only for your case though, not sure how generic it should be!

Resources