The XPath bookstore/book[1]
selects the first book node under bookstore
.
How can I select the first node that matches a more complicated condition, e.g. the first node that matches /bookstore/book[@location='US']
Use:
(/bookstore/book[@location='US'])[1]
This will first get the book elements with the location attribute equal to 'US'. Then it will select the first node from that set. Note the use of parentheses, which are required by some implementations.
Note, this is not the same as /bookstore/book[1][@location='US']
unless the first element also happens to have that location attribute.
/bookstore/book[@location='US'][1]
works only with simple structure.
Add a bit more structure and things break.
With-
<bookstore>
<category>
<book location="US">A1</book>
<book location="FIN">A2</book>
</category>
<category>
<book location="FIN">B1</book>
<book location="US">B2</book>
</category>
</bookstore>
/bookstore/category/book[@location='US'][1]
yields
<book location="US">A1</book>
<book location="US">B2</book>
not "the first node that matches a more complicated condition". /bookstore/category/book[@location='US'][2]
returns nothing.
With parentheses you can get the result the original question was for:
(/bookstore/category/book[@location='US'])[1]
gives
<book location="US">A1</book>
and (/bookstore/category/book[@location='US'])[2]
works as expected.
As an explanation to Jonathan Fingland's answer:
[position()=1 and @location='US']
) must be true as a whole[position()=1][@location='US']
) must be true one after another[position()=1][@location='US']
!= [@location='US'][position()=1]
[position()=1 and @location='US']
== [@location='US' and position()=1]
[position()=1]
can be abbreviated to [1]
You can build complex expressions in predicates with the Boolean operators "and
" and "or
", and with the Boolean XPath functions not()
, true()
and false()
. Plus you can wrap sub-expressions in parentheses.
The easiest way to find first english book node (in the whole document), taking under consideration more complicated structered xml file, like:
<bookstore>
<category>
<book location="US">A1</book>
<book location="FIN">A2</book>
</category>
<category>
<book location="FIN">B1</book>
<book location="US">B2</book>
</category>
</bookstore>
is xpath expression:
/descendant::book[@location='US'][1]
<bookstore>
<book location="US">A1</book>
<category>
<book location="US">B1</book>
<book location="FIN">B2</book>
</category>
<section>
<book location="FIN">C1</book>
<book location="US">C2</book>
</section>
</bookstore>
So Given the above; you can select the first book with
(//book[@location='US'])[1]
And this will find the first one anywhere that has a location US. [A1]
//book[@location='US']
Would return the node set with all books with location US. [A1,B1,C2]
(//category/book[@location='US'])[1]
Would return the first book location US that exists in a category anywhere in the document. [B1]
(/bookstore//book[@location='US'])[1]
will return the first book with location US that exists anywhere under the root element bookstore; making the /bookstore part redundant really. [A1]
In direct answer:
/bookstore/book[@location='US'][1]
Will return you the first node for book element with location US that is under bookstore [A1]
Incidentally if you wanted, in this example to find the first US book that was not a direct child of bookstore:
(/bookstore/*//book[@location='US'])[1]
With help of an online xpath tester I'm writing this answer...
For this:
<table id="t2"><tbody>
<tr><td>123</td><td>other</td></tr>
<tr><td>foo</td><td>columns</td></tr>
<tr><td>bar</td><td>are</td></tr>
<tr><td>xyz</td><td>ignored</td></tr>
</tbody></table>
the following xpath:
id("t2") / tbody / tr / td[1]
outputs:
123
foo
bar
xyz
Since 1 means select all td elements which are the first child of their own direct parent.
But the following xpath:
(id("t2") / tbody / tr / td)[1]
outputs:
123