Como selecionar o primeiro elemento com um atributo específico usando XPath

300

O XPath bookstore/book[1]seleciona o primeiro nó do livro em bookstore.

Como posso selecionar o primeiro nó que corresponde a uma condição mais complicada, por exemplo, o primeiro nó que corresponde /bookstore/book[@location='US']

ripper234
fonte

Respostas:

444

Usar:

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

Primeiro, os elementos do livro com o atributo de localização são iguais a 'US'. Em seguida, ele selecionará o primeiro nó desse conjunto. Observe o uso de parênteses, que são exigidos por algumas implementações.

Observe que isso não é o mesmo que /bookstore/book[1][@location='US'], a menos que o primeiro elemento também possua esse atributo de local.

Jonathan Fingland
fonte
Como eu poderia fazer o mesmo para // livraria / livro [@ location = 'US']?
Alexander V. Ilyin
7
Isso receberá todos os livros dos EUA. (/ livraria / livro [@ location = 'US']) [1] receberá o primeiro.
Kevin Driedger
3
O @KevinDriedger /bookstore/book[@location='US'][1]não retorna todos os livros de 'US'. Eu testei várias vezes e em implementações xpath de idiomas diferentes. /bookstore/book[@location='US'][1]retorna o primeiro livro dos EUA em uma livraria. Se houver várias livrarias, ele retornará a primeira de cada uma. Foi isso que o OP solicitou (o primeiro nó na livraria). Sua versão retorna apenas um livro de todas as livrarias (a primeira correspondência).
Jonathan Fingland
3
@ JonathanFingland você entendeu errado - leia a resposta de KevinDriedger novamente, juntamente com o contexto da pergunta de AlexanderV.Ilyin. Vocês dois querem dizer a mesma coisa.
Kryysktos
175

/bookstore/book[@location='US'][1] funciona apenas com estrutura simples.

Adicione um pouco mais de estrutura e as coisas quebram.

Com

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

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

não "o primeiro nó que corresponde a uma condição mais complicada". /bookstore/category/book[@location='US'][2]não retorna nada.

Com parênteses, você pode obter o resultado da pergunta original:

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

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

e (/bookstore/category/book[@location='US'])[2]funciona como esperado.

tkurki
fonte
11
Autor da resposta aceita aqui. A pergunta do OP considerada /bookstore/book[1]e NÃO (/bookstore/book)[1]. O caso que você forneceu não é o mesmo que o OP pediu. Presumivelmente, o OP aceitou minha resposta, como fez o que ele esperava (e solicitou).
Jonathan Fingland
Essa resposta me ajudou nesse caso peculiar. Alguém pode explicar por que não lida com "situações mais complicadas"? Desde basicamente ele encontrar uma lista com dois itens, o [2] deve apenas buscá-lo (no meu mundo)
Skurpi
Eu também acho essa resposta mais correta que a resposta selecionada, pois no meu caso, eu também tinha uma estrutura mais complexa onde simplesmente adicionava [1] vários nós retornados. Obrigado!
Mydoghasworms 18/09/12
2
Parênteses funciona! Você também pode adicionar mais caminho depois (..) [1], como: '(//div[text() = "'+ name +'"])[1]/following-sibling::*/div/text()'. Caso haja muitas correspondências de nós name.
Hlung
1
Estou mudando de opinião. Depois de alguma distância, entendi o que essa resposta estava dizendo e, se não visse o exemplo do OP, teria votado a favor. Suponho que estava reagindo ao tom dessa resposta; se @tkurki tivesse explicado um pouco mais sobre como separar a condição da seleção do primeiro nó, eu a teria visto instantaneamente. Talvez o mesmo para JonFingland.
precisa
51

Como explicação para a resposta de Jonathan Fingland:

  • várias condições no mesmo predicado ( [position()=1 and @location='US']) devem ser verdadeiras como um todo
  • várias condições em predicados consecutivos ( [position()=1][@location='US']) devem ser verdadeiras uma após a outra
  • isso implica que [position()=1][@location='US']! = [@location='US'][position()=1]
    while [position()=1 and @location='US']==[@location='US' and position()=1]
  • dica: um único [position()=1]pode ser abreviado para[1]

Você pode criar expressões complexas em predicados com os operadores booleanos " and" e " or" e com as funções booleanas XPath not(), true()e false(). Além disso, você pode agrupar sub-expressões entre parênteses.

Tomalak
fonte
15

A maneira mais fácil de encontrar o primeiro nó do livro em inglês (em todo o documento), levando em consideração o arquivo xml estruturado mais complicado, como:

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

é a expressão xpath:

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

Gee-Bee
fonte
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> 

Dado o exposto; você pode selecionar o primeiro livro com

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

E este encontrará o primeiro em qualquer lugar que tenha uma localização nos EUA. [A1]

//book[@location='US']

Retornaria o conjunto de nós com todos os livros com o local US. [A1, B1, C2]

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

Retornaria o primeiro local do livro nos EUA que existe em uma categoria em qualquer lugar do documento. [B1]

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

retornará o primeiro livro com o local US que existe em qualquer lugar na livraria de elementos raiz; tornando a livraria / parte realmente redundante. [A1]

Em resposta direta:

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

Retornará o primeiro nó para o elemento do livro com o local US que está na livraria [A1]

Aliás, se você quisesse, neste exemplo, encontre o primeiro livro dos EUA que não era filho direto da livraria:

(/bookstore/*//book[@location='US'])[1]
iZian
fonte
4

Use o índice para obter o nó desejado se o xpath for complicado ou se houver mais de um nó com o mesmo xpath.

Ex:

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

Você pode dar o número que nó deseja.

Mounika Medipelli
fonte
2

se o espaço para nome for fornecido no xml fornecido, é melhor usá-lo.

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

por ex.

<input b="demo">

E

(input[@b='demo'])[1]
SenthilKumarP
fonte
-1

Com a ajuda de um testador online do xpath , estou escrevendo esta resposta ...
Para isso:

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

o seguinte xpath:

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

saídas:

123
foo
bar
xyz

Como 1 significa selecionar todos os elementos td que são o primeiro filho de seu próprio pai direto.
Mas o seguinte xpath:

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

saídas:

123
Mohsen Abasi
fonte