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

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.


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

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

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


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.


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]


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]

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.


2

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

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


-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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.