Comment sélectionner le premier élément avec un attribut spécifique à l'aide de XPath


300

Le XPath bookstore/book[1]sélectionne le premier nœud de livre sousbookstore .

Comment puis-je sélectionner le premier nœud qui correspond à une condition plus compliquée, par exemple le premier nœud qui correspond /bookstore/book[@location='US']

Réponses:


444

Utilisation:

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

Cela obtiendra d'abord les éléments du livre avec l'attribut location égal à «US». Ensuite, il sélectionnera le premier nœud de cet ensemble. Notez l'utilisation de parenthèses, qui sont requises par certaines implémentations.

Remarque, ce n'est pas la même chose que /bookstore/book[1][@location='US']si le premier élément a également cet attribut location.


Comment pourrais-je faire de même pour // librairie / livre [@ location = 'US']?
Alexander V. Ilyin

7
Cela obtiendra tous les livres des États-Unis. (/ librairie / livre [@ location = 'US']) [1] obtiendra le premier.
Kevin Driedger

3
@KevinDriedger /bookstore/book[@location='US'][1]ne renvoie pas tous les livres des «États-Unis». Je l'ai testé plusieurs fois et sous des implémentations xpath de différents langages. /bookstore/book[@location='US'][1]renvoie le premier livre «américain» dans une librairie. S'il y a plusieurs librairies, elle retournera la première de chacune. C'est ce que l'OP a demandé (le premier nœud sous librairie). Votre version ne renvoie qu'un seul livre de toutes les librairies (le premier match).
Jonathan Fingland

3
@JonathanFingland vous avez mal compris - relisez la réponse de KevinDriedger, ainsi que le contexte de la question d'AlexanderV.Ilyin. Vous voulez dire tous les deux la même chose.
kiedysktos

175

/bookstore/book[@location='US'][1] ne fonctionne qu'avec une structure simple.

Ajoutez un peu plus de structure et les choses se cassent.

Avec

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

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

pas "le premier nœud qui correspond à une condition plus compliquée". /bookstore/category/book[@location='US'][2]ne renvoie rien.

Entre parenthèses, vous pouvez obtenir le résultat pour lequel la question d'origine était:

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

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

et (/bookstore/category/book[@location='US'])[2]fonctionne comme prévu.


11
Auteur de la réponse acceptée ici. La question du PO a été considérée /bookstore/book[1]et NON (/bookstore/book)[1]. Le cas que vous avez fourni n'est pas le même que celui demandé par OP. Vraisemblablement, OP a accepté ma réponse car elle a fait ce qu'il attendait (et a demandé).
Jonathan Fingland

Cette réponse m'a aidé dans ce cas particulier. Quelqu'un peut-il expliquer pourquoi il ne traitera pas des "situations plus compliquées"? Puisqu'en gros, il trouve une liste avec deux éléments, le [2] devrait juste le ramasser (dans mon monde)
Skurpi

Je trouve également que cette réponse est plus correcte que la réponse sélectionnée, car dans mon cas, j'avais également une structure plus complexe où simplement l'ajout de [1] renvoyait plusieurs nœuds. Merci!
mydoghasworms

2
Les parenthèses fonctionnent! Vous pouvez également ajouter chemin après (..), comme [1]: '(//div[text() = "'+ name +'"])[1]/following-sibling::*/div/text()'. Dans le cas où il y a plusieurs correspondances de nœuds name.
Hlung

1
Je change d'avis. Après une certaine distance, je comprends ce que disait cette réponse, et si je ne voyais pas l'exemple du PO, j'aurais voté pour cela. Je suppose que je réagissais au ton de cette réponse; si @tkurki avait expliqué un peu plus sur la séparation de la condition de la sélection du premier nœud, je l'aurais vu instantanément. Peut-être la même chose pour JonFingland.
Gerard ONeill

51

Pour expliquer la réponse de Jonathan Fingland:

  • plusieurs conditions dans le même prédicat ( [position()=1 and @location='US']) doivent être vraies dans leur ensemble
  • plusieurs conditions dans des prédicats consécutifs ( [position()=1][@location='US']) doivent être vraies l' une après l'autre
  • cela implique que [position()=1][@location='US']! = [@location='US'][position()=1]
    while [position()=1 and @location='US']==[@location='US' and position()=1]
  • indice: un seul [position()=1]peut être abrégé en[1]

Vous pouvez construire des expressions complexes prédicats avec les opérateurs booléens « and» et « or», et avec les fonctions XPath booléenne not(), true()et false(). De plus, vous pouvez mettre des sous-expressions entre parenthèses.


15

Le moyen le plus simple de trouver le premier nœud de livre en anglais (dans tout le document), en tenant compte d'un fichier xml structuré plus compliqué, comme:

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

est l'expression 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> 

Compte tenu de ce qui précède; vous pouvez sélectionner le premier livre avec

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

Et cela trouvera le premier n'importe où qui a un emplacement aux États-Unis. [A1]

//book[@location='US']

Renvoyer l'ensemble de nœuds avec tous les livres avec l'emplacement US. [A1, B1, C2]

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

Renvoyer le premier emplacement de livre US qui existe dans une catégorie n'importe où dans le document. [B1]

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

retournera le premier livre avec l'emplacement US qui existe n'importe où sous la librairie de l'élément racine; rendant vraiment la partie / librairie redondante. [A1]

En réponse directe:

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

Vous renverra le premier nœud de l'élément livre avec l'emplacement US qui se trouve sous la librairie [A1]

Soit dit en passant si vous vouliez, dans cet exemple, trouver le premier livre américain qui n'était pas un enfant direct de la librairie:

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

4

Utilisez l'index pour obtenir le nœud souhaité si xpath est compliqué ou si plusieurs nœuds sont présents avec le même xpath.

Ex:

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

Vous pouvez donner le numéro du nœud que vous souhaitez.


2

si l'espace de noms est fourni sur le xml donné, il vaut mieux l'utiliser.

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


-1

Avec l'aide d'un testeur xpath en ligne, j'écris cette réponse ...
Pour cela:

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

le xpath suivant:

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

les sorties:

123
foo
bar
xyz

Puisque 1 signifie sélectionner tous les éléments td qui sont le premier enfant de leur propre parent direct.
Mais le xpath suivant:

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

les sorties:

123
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.