Jak wybrać pierwszy element z określonym atrybutem za pomocą XPath

300

XPath bookstore/book[1]wybiera pierwszy węzeł książki pod bookstore.

Jak mogę wybrać pierwszy węzeł, który pasuje do bardziej skomplikowanego warunku, np. Pierwszy, który pasuje /bookstore/book[@location='US']

ripper234
źródło

Odpowiedzi:

444

Posługiwać się:

(/bookstore/book[@location='US'])[1]

Najpierw otrzyma elementy książki z atrybutem położenia równym „US”. Następnie wybierze pierwszy węzeł z tego zestawu. Zwróć uwagę na użycie nawiasów, które są wymagane przez niektóre implementacje.

Uwaga: nie jest to to samo, o /bookstore/book[1][@location='US']ile pierwszy element również nie ma tego atrybutu położenia.

Jonathan Fingland
źródło
Jak mogłem zrobić to samo dla // bookstore / book [@ location = 'US']?
Alexander V. Ilyin,
7
Otrzymasz wszystkie książki z „US”. (/ bookstore / book [@ location = 'US']) [1] dostanie pierwszy.
Kevin Driedger
3
@KevinDriedger /bookstore/book[@location='US'][1]nie zwraca wszystkich książek z „US”. Testowałem to wielokrotnie i pod różnymi implementacjami xpath w różnych językach. /bookstore/book[@location='US'][1]zwraca pierwszą książkę „US” pod księgarnią. Jeśli istnieją różne księgarnie, zwróci pierwszą z każdej. O to poprosił OP (pierwszy węzeł w księgarni). Twoja wersja zwraca tylko jedną książkę ze wszystkich księgarń (pierwszy mecz).
Jonathan Fingland
3
@JonathanFingland, który źle zrozumiałeś - przeczytaj ponownie odpowiedź KevinaDriedgera wraz z kontekstem pytania AlexanderV.Ilyin. Oboje macie na myśli to samo.
kiedysktos
175

/bookstore/book[@location='US'][1] działa tylko z prostą strukturą.

Dodaj trochę więcej struktury i rzeczy się psują.

Z

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

<book location="US">A1</book>
<book location="US">B2</book>

nie „pierwszy węzeł, który pasuje do bardziej skomplikowanego warunku”. /bookstore/category/book[@location='US'][2]nic nie zwraca.

Za pomocą nawiasów można uzyskać wynik pierwotnego pytania:

(/bookstore/category/book[@location='US'])[1] daje

<book location="US">A1</book>

i (/bookstore/category/book[@location='US'])[2]działa zgodnie z oczekiwaniami.

tkurki
źródło
11
Autor zaakceptowanej odpowiedzi tutaj. Pytanie OP rozważone /bookstore/book[1]i NIE (/bookstore/book)[1]. Podany przez Ciebie przypadek nie jest taki sam jak ten, o który prosiłeś PO. Przypuszczalnie OP zaakceptował moją odpowiedź, ponieważ spełnił oczekiwania (i poprosił).
Jonathan Fingland
Podana odpowiedź pomogła mi w tej szczególnej sprawie. Czy ktoś może wyjaśnić, dlaczego nie poradzi sobie z „bardziej skomplikowanymi sytuacjami”? Ponieważ w zasadzie znajduje listę z dwoma przedmiotami, [2] powinien ją po prostu podnieść (w moim świecie)
Skurpi
Uważam również, że ta odpowiedź jest bardziej poprawna niż wybrana odpowiedź, ponieważ w moim przypadku miałem również bardziej złożoną strukturę, w której zwykłe dodanie [1] zwróciło wiele węzłów. Dzięki!
mydoghasworms
2
Nawiasy działają! Można również dodać więcej ścieżkę po (..) [1], jak: '(//div[text() = "'+ name +'"])[1]/following-sibling::*/div/text()'. W przypadku wielu dopasowań węzłów name.
Hlung,
1
Zmieniam zdanie. Po pewnym dystansie rozumiem, co mówiła ta odpowiedź, a jeśli nie widziałbym przykładu PO, głosowałbym na to. Przypuszczam, że reagowałem na ton tej odpowiedzi; Gdyby @tkurki wyjaśnił nieco więcej na temat oddzielenia warunku od wyboru pierwszego węzła, natychmiast bym go zobaczył. Być może to samo dla JonFinglanda.
Gerard ONeill,
51

Jako wyjaśnienie odpowiedzi Jonathana Finglanda:

  • wiele warunków w tym samym predykacie ( [position()=1 and @location='US']) musi być spełnionych jako całość
  • wiele warunków w kolejnych predykatach ( [position()=1][@location='US']) musi być spełnionych jeden po drugim
  • oznacza to, że [position()=1][@location='US']! = [@location='US'][position()=1]
    while [position()=1 and @location='US']==[@location='US' and position()=1]
  • wskazówka: samotny [position()=1]można skrócić[1]

Możesz budować złożone wyrażenia w predykatach za pomocą operatorów boolowskich „ and” i „ or” oraz funkcji boolowskich XPath not(), true()i false(). Dodatkowo możesz zawijać wyrażenia w nawiasach.

Tomalak
źródło
15

Najłatwiejszy sposób na znalezienie pierwszego angielskiego węzła książki (w całym dokumencie), biorąc pod uwagę bardziej skomplikowany plik XML, taki jak:

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

to wyrażenie xpath:

/descendant::book[@location='US'][1]

Gee-Bee
źródło
10
    <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> 

Biorąc pod uwagę powyższe; możesz wybrać pierwszą książkę za pomocą

(//book[@location='US'])[1]

I to znajdzie pierwszy gdziekolwiek, który ma lokalizację USA. [A1]

//book[@location='US']

Zwróci zestaw węzłów ze wszystkimi książkami o lokalizacji US. [A1, B1, C2]

(//category/book[@location='US'])[1]

Zwraca pierwszą lokalizację książki US, która istnieje w kategorii w dowolnym miejscu w dokumencie. [B1]

(/bookstore//book[@location='US'])[1]

zwróci pierwszą książkę z lokalizacją US, która istnieje w dowolnym miejscu pod księgarnią elementu głównego; sprawiając, że część / bookstore stała się zbędna. [A1]

W bezpośredniej odpowiedzi:

/bookstore/book[@location='US'][1]

Zwróci ci pierwszy węzeł dla elementu książki z lokalizacją US, która jest pod księgarnią [A1]

Nawiasem mówiąc, jeśli chcesz, w tym przykładzie znajdź pierwszą amerykańską książkę, która nie była bezpośrednim dzieckiem księgarni:

(/bookstore/*//book[@location='US'])[1]
iZian
źródło
4

Użyj indeksu, aby uzyskać żądany węzeł, jeśli xpath jest skomplikowany lub więcej niż jeden węzeł ma tę samą ścieżkę xpath.

Np .:

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

Możesz podać numer żądanego węzła.

Mounika Medipelli
źródło
2

jeśli przestrzeń nazw jest podana na danym xml, lepiej tego użyć.

(/*[local-name() ='bookstore']/*[local-name()='book'][@location='US'])[1]
Ed Bangga
źródło
0

na przykład

<input b="demo">

I

(input[@b='demo'])[1]
SenthilKumarP
źródło
-1

Przy pomocy internetowego testera xpath piszę tę odpowiedź ...
W tym celu:

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

następujący xpath:

id("t2") / tbody / tr / td[1]

wyjścia:

123
foo
bar
xyz

Ponieważ 1 oznacza wybierz wszystkie elementy td , które są pierwszym dzieckiem własnego bezpośredniego rodzica.
Ale następujący xpath:

(id("t2") / tbody / tr / td)[1]

wyjścia:

123
Mohsen Abasi
źródło