ripper234
ripper234

Reputation: 229988

How to select the first element with a specific attribute using XPath

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']

Upvotes: 404

Views: 619419

Answers (9)

tkurki
tkurki

Reputation: 2089

/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.

Upvotes: 208

Jonathan Fingland
Jonathan Fingland

Reputation: 57157

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.

Upvotes: 585

Mounika Medipelli
Mounika Medipelli

Reputation: 61

Use the index to get desired node if xpath is complicated or more than one node present with same xpath.

Ex :

(//bookstore[@location = 'US'])[index]

You can give the number which node you want.

Upvotes: 5

Ed Bangga
Ed Bangga

Reputation: 13006

if namespace is provided on the given xml, its better to use this.

(/*[local-name() ='bookstore']/*[local-name()='book'][@location='US'])[1]

Upvotes: 2

محسن عباسی
محسن عباسی

Reputation: 2424

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

Upvotes: 0

SenthilKumarP
SenthilKumarP

Reputation: 59

for ex.

<input b="demo">

And

(input[@b='demo'])[1]

Upvotes: 0

iZian
iZian

Reputation: 363

    <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]

Upvotes: 12

Tomalak
Tomalak

Reputation: 338108

As an explanation to Jonathan Fingland's answer:

  • multiple conditions in the same predicate ([position()=1 and @location='US']) must be true as a whole
  • multiple conditions in consecutive predicates ([position()=1][@location='US']) must be true one after another
  • this implies that [position()=1][@location='US'] != [@location='US'][position()=1]
    while [position()=1 and @location='US'] == [@location='US' and position()=1]
  • hint: a lone [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.

Upvotes: 63

Gee-Bee
Gee-Bee

Reputation: 3195

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]

Upvotes: 16

Related Questions