Compare attribute of one element to attribute of another element - xpath

This seems like it should be easy, but I can never figure it out.
Presume I have the following document:
<data>
<a>
<b val="1"/>
</a>
<c val="1">
</data>
And assume that I am executing an XPath from the context of <b>. I need to check if there is an element c that has the same value as b.
Obviously, this doesn't work:
../a/c[#val=#val]
How to I get an XPath to remember its "current" context when traversing the tree?

Try the expression below. You'll notice that the current node is not lost since a predicate is used for finding the c node.
.[../../c/#val=#val]

Related

Using XPATH previous:: more like an array

I've got XML like this
<root>
...
<a>
<a>
<a>
<c>
...
It's very flat with LOTS of A elements and a few C elements. The A elements are sensor data and the last reading is bogus, I need the one before. So I'd like to use the C elements as a marker and each of A elements 2 before each C. So I'm trying out an XPATH like:
/root/c/preceding-sibling::a
but I'm getting all previous A elements, I was hoping for something a bit more direct such as:
/root/c/preceeding-sibling[-2]
which would just grab the 2nd sibling before C (no matter the type) I guess I'm asking for array like functionality on an XPATH so what ever I match I can ask for "the second element before that"
Is this possible?
You can
just grab the 2nd sibling before C (no matter the type)
with the XPath expression
/root/c/preceding-sibling::*[2]
The node count for preceding-sibling:: is going backwards. The node with the index [1] is the node before c and the node with the index [2] is the node before this - which is
the second element before that

Xpath having multiple predicate statements

I've looked around and can't seem to find the answer for this.
Very simplified:
<a>
<b>
<div class=label>
<label="here"/>
</div>
</b>
<div id="something">
<b>
<div class=label>
<label="here"/>
</div>
</div>
</a>
so I'm trying to grab the second "here" label. What I want to do is do the id to get to the "something" part
//.[#id="something”]
and then from that point search for the label with something like
//.[#class="label" and label="here"]
But from reading a few other replies it doesn't appear that something like
//.[#id="something”][#class="label" and label="here"]
works and I was just wondering if I'm just missing something in how it's working? I know I can get the above really simply with another method, it's just an example to ask how to do two predicate statements after each other (if it is indeed possible).
Thanks!
I think you need something like this instead :
//.[#id="something”]//.[#class="label" and label="here"]
The point is that the // means : Selects nodes in the document from the current node that match the selection no matter where they are
ref : http://www.w3schools.com/xpath/xpath_syntax.asp
The syntax //*[#x='y'] is more idiomatic than //.[#x='y'], probably because it's valid in both XPath 1.0 and XPath 2.0, whereas the latter is only allowed in XPath 2.0. Disallowing predicates after "." was probably an accidental restriction in XPath 1.0, and I think some implementations may have relaxed the restriction, but it's there in the spec: a predicate can only follow either a NodeTest or a PrimaryExpr, and "." is neither.
In XPath 2.0, //* selects all element nodes in the tree, while //. selects all nodes of all kinds (including the document root node), but in this example the effect is the same because the predicate [#x='y'] can only be matched by an element node (for all other node kinds, #x selects nothing and therefore cannot be equal to anything).

Looking for n-th instance of x node in root node

Suppose I have following xml
<root>
<x>
<y />
<z>
<y />
</z>
<n>
<m>
<y />*
</m>
</n>
</x>
<x>
<y />
<z>
<y />
</z>
<y />*
</x>
</root>
I would like to retrieve those y nodes which are followed with *
So it is always third node in x ancestor node
I tried something like:
//x//y[3]
However it doesn't work I guess it would work only if y nodes are on the same level.
So I tried
(//x//y)[3] but it retrieves only one node (third one) in whole document
So I tried something like:
//x(//y)[3]
//x(//y[3])
//x//(y[3])
etc. but I get parse error
Is there any way to retrieve what I need using xpath?
Use:
//x/descendant::y[3]
This selects every third y descendant of each x in the document. It sometimes helps to write out an expanded expression to see what's really going on. In this case, the following:
//x//y[3]
is equivalent to:
/descendant-or-self::node()/child::x/descendant-or-self::node()/child::y[3]
Written this way it becomes obvious why it doesn't do what you wanted (i.e. it's looking for any y that is the third child of an x element and there isn't one). What you really wanted was every third y descendant. Here it is fully expanded:
/descendant-or-self::node()/child::x/descendant::y[3]
The important lesson here is that it pays to know what the XPath abbreviated syntax is really doing. The spec is actually quite readable. I recommend taking a look.
Update: both of these examples are XPath 2.0 only.
In XPath 1.0:
/row//y/(ancestor::x//y)[3]
In XPath 2.0:
for $x in /row//x
return ($x//y)[3]

Xpath expression to find non-child elements by attribute

here's a nice puzzle. Suppose we have this bit of code:
<page n="1">
<line n="3">...</line>
</page>
It is real easy to locate the line element "n=3" within the page element "n=1" with a simple xpath expression: xpath(//page[#n='1')/line[#n='3']). Great, beautiful, elegant.
Now suppose what we have is this encoding (folks familiar with the TEI will know where this is coming from).
<pb n="1"/>
(arbitrary amounts of stuff)
<lb n="3"/>
We want to find the lb element with n="3", which follows the pb element with n="1". But note -- this lb element could be almost anywhere following the pb: it may not be (and most likely is not) a sibling, but could be a child of a sibling of the pb, or of the pb's parent, etc etc etc.
So my question: how would you search for this lb element with n="3", which follows the pb element with n="1", with XPath?
Thanks in advance
Peter
Use:
//pb[#n='1']/following::lb[#n='2']
|
//pb[#n='1']/descendant::lb[#n='2']
This selects any lb element that follows the specified pb in document order -- even if the wanted lb element is a descendant of the pb element.
Do note that the following expression doesn't in general select all wanted lb elements (it fails to select any of these that are descendants of the pb element):
//pb[#n='1']/following::lb[#n='2']
Explanation:
As defined in the W3C XPath specification, the following:: and descendant:: axes are non-overlapping:
"the following axis contains all nodes in the same document as the
context node that are after the context node in document order,
excluding any descendants and excluding attribute nodes and namespace nodes"
That would be
//pb[#n=1]/following::lb[#n=3]

Xpath to select only nodes where child elements exist?

This should be an easy one but it is giving me trouble. Given this structure:
<root>
<a>
<b/>
</a>
<a/>
</root>
I'm trying to formulate an xpath expression that gives only the non-empty "a" elements, i.e. the ones that have child elements. Therefore I want the first instance of "a" returned, but not the second.
So far I have "/root/a/self::*" but that is returning me both a's.
/root/a[count(*)>0]
will give any 'a' node with any kind of child node
/root/a[count(*)>0]
This one works
/root/a[*]
or even
//a[*]

Resources