Cómo seleccionar el primer elemento con un atributo específico usando XPath

300

XPath bookstore/book[1]selecciona el primer nodo del libro en bookstore.

¿Cómo puedo seleccionar el primer nodo que coincide con una condición más complicada, por ejemplo, el primer nodo que coincide /bookstore/book[@location='US']

ripper234
fuente

Respuestas:

444

Utilizar:

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

Primero obtendrá los elementos del libro con el atributo de ubicación igual a 'US'. Luego seleccionará el primer nodo de ese conjunto. Tenga en cuenta el uso de paréntesis, que algunas implementaciones requieren.

Tenga en cuenta que esto no es lo mismo que a /bookstore/book[1][@location='US']menos que el primer elemento también tenga ese atributo de ubicación.

Jonathan Fingland
fuente
¿Cómo podría hacer lo mismo para // librería / libro [@ location = 'US']?
Alexander V. Ilyin
77
Esto obtendrá todos los libros de "EE. UU.". (/ bookstore / book [@ location = 'US']) [1] obtendrá el primero.
Kevin Driedger
3
@KevinDriedger /bookstore/book[@location='US'][1]no devuelve todos los libros de 'EE. UU.'. Lo he probado varias veces y en implementaciones xpath de diferentes idiomas. /bookstore/book[@location='US'][1]devuelve el primer libro "EE. UU." en una librería. Si hay varias librerías, devolverá la primera de cada una. Esto es lo que solicitó el OP (el primer nodo en la librería). Su versión solo devuelve un libro de todas las librerías (el primer partido).
Jonathan Fingland
3
@JonathanFingland que entendiste mal: lee la respuesta de KevinDriedger nuevamente, junto con el contexto de la pregunta de AlexanderV.Ilyin. Ambos quieren decir lo mismo.
kiedysktos
175

/bookstore/book[@location='US'][1] funciona solo con estructura simple.

Agregue un poco más de estructura y las cosas se rompen.

Con

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

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

no "el primer nodo que coincide con una condición más complicada". /bookstore/category/book[@location='US'][2]no devuelve nada

Con paréntesis puede obtener el resultado de la pregunta original:

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

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

y (/bookstore/category/book[@location='US'])[2]funciona como se esperaba.

tkurki
fuente
11
Autor de la respuesta aceptada aquí. La pregunta del OP considerada /bookstore/book[1]y NO (/bookstore/book)[1]. El caso que proporcionó no es el mismo que el que solicitó OP. Presumiblemente, OP aceptó mi respuesta ya que hizo lo que esperaba (y solicitó).
Jonathan Fingland
Esta respuesta proporcionada me ayudó para este caso peculiar. ¿Alguien puede explicar por qué no manejará "situaciones más complicadas"? Dado que básicamente encuentra una lista con dos elementos, el [2] debería simplemente recogerla (en mi mundo)
Skurpi
También encuentro que esta respuesta es más correcta que la respuesta seleccionada, ya que en mi caso, también tenía una estructura más compleja en la que simplemente agregar [1] devolvía múltiples nodos. ¡Gracias!
mydoghasworms
2
Paréntesis funciona! También puede añadir más trayectoria después de (..) [1], como: '(//div[text() = "'+ name +'"])[1]/following-sibling::*/div/text()'. En caso de que haya muchos nodos coincidentes name.
Hlung
1
Estoy cambiando mi opinion. Después de cierta distancia, entiendo lo que decía esta respuesta, y si no veía el ejemplo del OP, habría votado a favor. Supongo que estaba reaccionando al tono de esta respuesta; Si @tkurki hubiera explicado un poco más sobre la separación de la condición de la selección del primer nodo, lo habría visto al instante. Quizás lo mismo para JonFingland.
Gerard ONeill
51

Como explicación a la respuesta de Jonathan Fingland:

  • múltiples condiciones en el mismo predicado ( [position()=1 and @location='US']) deben ser verdaderas como un todo
  • múltiples condiciones en predicados consecutivos ( [position()=1][@location='US']) deben ser verdaderas una tras otra
  • esto implica que [position()=1][@location='US']! = [@location='US'][position()=1]
    while [position()=1 and @location='US']==[@location='US' and position()=1]
  • pista: un solitario [position()=1]se puede abreviar a[1]

Puede construir expresiones complejas en predicados con los operadores booleanos " and" y " or", y con las funciones booleanas XPath not(), true()y false(). Además, puede ajustar las subexpresiones entre paréntesis.

Tomalak
fuente
15

La forma más fácil de encontrar el primer nodo de libro en inglés (en todo el documento), teniendo en cuenta un archivo XML estructurado más 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> 

es la expresión xpath:

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

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

Entonces, dado lo anterior; puedes seleccionar el primer libro con

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

Y este encontrará el primero en cualquier lugar que tenga una ubicación en EE. UU. [A1]

//book[@location='US']

Devolvería el conjunto de nodos con todos los libros con ubicación US. [A1, B1, C2]

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

Devolvería la primera ubicación de libro de EE. UU. Que existe en una categoría en cualquier parte del documento. [B1]

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

devolverá el primer libro con la ubicación de EE. UU. que existe en cualquier lugar debajo de la librería de elementos raíz; haciendo que la parte / bookstore sea realmente redundante. [A1]

En respuesta directa:

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

Le devolverá el primer nodo para el elemento del libro con la ubicación US que está debajo de la librería [A1]

Por cierto, si quisieras, en este ejemplo, encontrar el primer libro de EE. UU. Que no fue hijo directo de una librería:

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

Use el índice para obtener el nodo deseado si xpath es complicado o si hay más de un nodo presente con el mismo xpath.

Ej:

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

Puede dar el número del nodo que desea.

Mounika Medipelli
fuente
2

si se proporciona espacio de nombres en el xml dado, es mejor usarlo.

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

por ej.

<input b="demo">

Y

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

Con la ayuda de un probador de xpath en línea , estoy escribiendo esta respuesta ...
Para esto:

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

el siguiente xpath:

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

salidas:

123
foo
bar
xyz

Como 1 significa seleccionar todos los elementos td que son el primer hijo de su propio padre directo.
Pero el siguiente xpath:

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

salidas:

123
Mohsen Abasi
fuente