I'm trying to test if an attribute on an ancestor of an element not equal a string.
Here is my XML...
<aaa att="xyz">
<bbb>
<ccc/>
</bbb>
</aaa>
<aaa att="mno">
<bbb>
<ccc/>
</bbb>
</aaa>
If I'm acting on element ccc, I'm trying to test that its grandparent aaa #att doesn't equal "xyz".
I currently have this...
ancestor::aaa[not(contains(#att, 'xyz'))]
Thanks!
Assuming that by saying an ancestor of an element you're referring to an element with child elements, this XPath expression should do:
//*[*/ccc][#att != 'xyz']
It selects
all nodes
that have at least one <ccc> grandchild node
and that have an att attribute whose value is not xyz.
Update: Restricted test to grandparents of <ccc>.
Update 2: Adapted to your revised question:
//ccc[../parent::aaa/#att != 'xyz']
Selects
all <ccc> elements
that have a grandparent <aaa> with its attribute att set to a value that is not xyz
Related
In this xpath:
/A/B[C='hello']
Is C="hello" some kind of syntactic shortcut for C[text()='hello']? Is it documented anywhere?
Edit: Okay, I discovered one difference: C= returns all the text nodes in C and C's children, while C[text()= returns only the text nodes in C.
Now, suppose I have the XML:
<root>
<A>
<B>
<C>hello<E>EEE</E>world</C>
<D>world</D>
</B>
<B>
<C>goodbye</C>
<D>mars</D>
</B>
</A>
</root>
How would I choose the B node containing the first C node using the syntax C[text()=? I can get the B node using the C= syntax like this:
/root/A/B[C="helloEEEworld"]
But this doesn't work:
/root/A/B[C[text()="helloworld"]]
nor do these:
/root/A/B[C[text()="hello world"]]
/root/A/B[C[text()="helloEEEworld"]]
Hmmm...this works:
/root/A/B[C[text()="hello"]]
Why is that? Does text() only return the first text node? According to the W3C, text() returns all text node children of the context node.
text() really returns all text node children as list of nodes
When you use /root/A/B[C[text()="hello"]] you mean fetch B node with C child that any direct child node is equal to "hello".
In the same way you can match it by :
/root/A/B[C[text()="world"]]
or explicitly specify that you want to get node by exact first or second direct child text node:
/root/A/B[C[text()[1]="hello"]]
/root/A/B[C[text()[2]="world"]]
If you want to match required node by its complete text content you can use
/root/A/B[C[.="helloEEEworld"]]
or
/root/A/B[C="helloEEEworld"]
C in the predicate expression [C='hello'] returns all C elements that is direct child of context element which is B. So the entire predicate is a boolean expression that contains comparison between a node-set and a string (notice that element is a type of node in XPath data model), and behavior of this case is documented in the spec as follows :
If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the string-value of the node and the other string is true. If one object to be compared is a node-set and the other is a boolean, then the comparison will be true if and only if the result of performing the comparison on the boolean and on the result of converting the node-set to a boolean using the boolean function is true. [source]
C='hello' in /A/B[C='hello'] will be evaluated to true if any of the C elements, after converted to string, equals 'hello'. So it is more of a shortcut for C[string()='hello'] if you will.
"Hmmm...this works:
/root/A/B[C[text()="hello"]]
Why is that? Does text() only return the first text node? According to the W3C, text() returns all text node children of the context node."
Instead of the first text node, text() in this context returns all direct child text nodes. This is because child:: is the default axis in XPath. Contrasts your XPath with the equivalent verbose version of it :
/child::root/child::A/child::B[child::C[child::text()="hello"]]
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.
I need an XPath (1.0) to get all parent nodes with multiple children but only one type of child (e.g., either <div> or <li> but not <div> and <li>). Any help? Thank you!
<doc>
<tom>
<janet />
</tom>
<dick>
<janet />
<jane />
</dick>
<harry>
<jane />
</harry>
</doc>
So for the above we should get tom and harry but not dick
Using the example as a reference, the following XPath 1.0 expression:
/doc/*[count(./*) = count(./*[name(.) = name(../*[1])])]
Will return all children of doc where the total number of children of that element equals the number of children with the same name as the first child of that element. Or, more simply put, all children have the same name aka 'type'.
However, the above will return nodes that have 0 or 1 children, so to restrict it to only those where there are multiple child nodes, we can use:
/doc/*[count(./*) = count(./*[name(.) = name(../*[1])]) and count(./*) > 1]
If you want to further restrict it so that all children have to be a certain element, for example jane, you could use: /doc/*[count(./*) = count(./*[name(.) = name(../*[1])]) and count(./*) > 1 and ./*[1] = ./jane[1]]
I'm looking for an xpath that will give me a child node only if the parent node doesn't equal a specific value. For example if I have an xml like the following:
<Grandpa><Dad><Son /></Dad><Son /></Grandpa>
I want to return the Son element outside the Dad element.
This Xpath selects those Son elements whose parent element is not named Dad:
//Son[local-name(..) != 'Dad']
So, applied to this XML:
<Grandpa><Dad><Son a="1"/></Dad><Son a="2"/></Grandpa>
It will select:
<Son a="2"/>
I have XML like this:
<AAA>
<BBB aaa="111" bbb="222">
<CCC/>
<CCC xxx="555" yyy="666" zzz="777"/>
</BBB>
<BBB aaa="999">
<CCC xxx="qq"/>
<DDD xxx="ww"/>
<EEE xxx="oo"/>
</BBB>
<BBB>
<DDD xxx="oo"/>
</BBB>
</AAA>
I want to get first <CCC> element. But with XPath expression //*/CCC[1] I have got two <CCC> elements. Each of them is the first elemet in <BBB></BBB> context. How to get first element in subset?
This one should work for you:
(//*/CCC)[1]
I want to get first element. But with
XPath expression //*/CCC[1] I have
got two elements. Each of them is the
first elemet in <BBB></BBB> context.
How to get first element in subset?
This is a FAQ:
The [] operator has a higher precedence (binds stronger) than the // abbreviation.
Use:
(//CCC)[1]
This selects the first (in document order) CCC element in the XML document.