Obtener nodos donde el nodo hijo contiene un atributo

116

Supongamos que tengo el siguiente XML:

<book category="CLASSICS">
  <title lang="it">Purgatorio</title>
  <author>Dante Alighieri</author>
  <year>1308</year>
  <price>30.00</price>
</book>

<book category="CLASSICS">
  <title lang="it">Inferno</title>
  <author>Dante Alighieri</author>
  <year>1308</year>
  <price>30.00</price>
</book>

<book category="CHILDREN">
  <title lang="en">Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

<book category="WEB">
  <title lang="en">XQuery Kick Start</title>
  <author>James McGovern</author>
  <author>Per Bothner</author>
  <author>Kurt Cagle</author>
  <author>James Linn</author>
  <author>Vaidyanathan Nagarajan</author>
  <year>2003</year>
  <price>49.99</price>
</book>

<book category="WEB">
  <title lang="en">Learning XML</title>
  <author>Erik T. Ray</author>
  <year>2003</year>
  <price>39.95</price>
</book>

Me gustaría hacer un xpath que recupere todos los nodos de libros que tienen un nodo de título con un atributo de idioma de "eso".

Mi intento se parecía a esto:

//book[title[@lang='it']]

Pero eso no funcionó. Espero recuperar los nodos:

<book category="CLASSICS">
  <title lang="it">Purgatorio</title>
  <author>Dante Alighieri</author>
  <year>1308</year>
  <price>30.00</price>
</book>

<book category="CLASSICS">
  <title lang="it">Inferno</title>
  <author>Dante Alighieri</author>
  <year>1308</year>
  <price>30.00</price>
</book>

¿Alguna pista?

Uwe Keim
fuente
¿Qué implementación de XPath es esta?
Pavel Minaev

Respuestas:

175

Tratar

//book[title/@lang = 'it']

Esto dice:

  • obtener toda book elementos
    • que tienen al menos uno title
      • que tiene un atributo lang
        • con un valor de "it"

Usted puede encontrar este útil - es un artículo titulado "XPath en cinco párrafos" por Ronald Bourret.

Pero con toda honestidad, //book[title[@lang='it']]y lo anterior debería ser equivalente, a menos que su motor XPath tenga "problemas". Por lo tanto, podría ser algo en el código o XML de muestra que no nos está mostrando; por ejemplo, su muestra es un fragmento XML. ¿Podría ser que el elemento raíz tiene un espacio de nombres y no lo está contando en su consulta? Y solo nos dijo que no funcionó, pero no nos dijo qué resultados obtuvo.

lavinio
fuente
4
¿Cómo hacer lo mismo si titleno es hijo directo de book, sino en algún lugar más profundo y no sabemos dónde exactamente? //book[/title/@lang = 'it']no parece funcionar?
Martin Konicek
5
Martin, podrías usar //book[.//title/@lang = 'it']. Creo que el truco es el "." al comienzo de la condición.
Bruno Caponi
1
Gracias por el enlace, excelente artículo. He estado usando xPath durante años, ¡pero esto realmente me ayudó a comprender la lógica subyacente!
Swensor
57

Años más tarde, pero una opción útil sería utilizar XPath Axes ( https://www.w3schools.com/xml/xpath_axes.asp ). Más específicamente, está buscando usar los descendientes ejes .

Creo que este ejemplo haría el truco:

//book[descendant::title[@lang='it']]

Esto le permite seleccionar todos los bookelementos que contienen un niño.title elemento (independientemente de qué tan profundo esté anidado) que contengan un valor de atributo de idioma igual a 'eso'.

No puedo decir con certeza si esta respuesta es relevante para el año 2009, ya que no estoy 100% seguro de que XPath Axes existiera en ese momento. Lo que puedo confirmar es que existen hoy y los he encontrado extremadamente útiles en la navegación XPath y estoy seguro de que usted también lo hará.

Wes.hysell
fuente
12
//book[title[@lang='it']]

es realmente equivalente a

 //book[title/@lang = 'it']

Lo probé usando vtd-xml, ambas expresiones arrojan el mismo resultado ... ¿qué motor de procesamiento xpath usaste? Supongo que tiene un problema de conformidad A continuación se muestra el código

import com.ximpleware.*;
public class test1 {
  public static void main(String[] s) throws Exception{
      VTDGen vg = new VTDGen();
      if (vg.parseFile("c:/books.xml", true)){
          VTDNav vn = vg.getNav();
          AutoPilot ap = new AutoPilot(vn);
          ap.selectXPath("//book[title[@lang='it']]");
                  //ap.selectXPath("//book[title/@lang='it']");

          int i;
          while((i=ap.evalXPath())!=-1){
              System.out.println("index ==>"+i);
          }
          /*if (vn.endsWith(i, "< test")){
             System.out.println(" good ");  
          }else
              System.out.println(" bad ");*/

      }
  }
}
vtd-xml-author
fuente
+1 que es un problema de cumplimiento y que la sintaxis genera el mismo conjunto de nodos. También funciona un código similar en C #.
Zach Bonham
-1: Sr. Zhang, estaba tratando de hacerle un favor al eliminar el código que no es relevante para la pregunta. Me permitió no votarte negativamente, lo que ahora siento que debo hacerlo. Tenga en cuenta que ninguna otra respuesta incluyó código para llamar a la consulta.
John Saunders
6
+1: Porque no puedo entender de qué está hablando el Sr. Saunders; ninguna otra respuesta agregó NINGÚN código, y esta respuesta muestra el código utilizado para que podamos 1: validar sus métodos y 2: realizar su prueba nosotros mismos. El código es breve y fácil de leer. No veo el problema.
DuckPuppy
4

Creo que su propia sugerencia es correcta, sin embargo, el xml no es del todo válido. Si está ejecutando la //book[title[@lang='it']]de <root>[Your"XML"Here]</root>entonces los probadores libres XPath en línea, tales como uno que aquí se encuentra el resultado esperado.

Joakim Byg
fuente
2

Intente usar esta expresión xPath:

//book/title[@lang='it']/..

Eso debería proporcionarle todos los nodos de libros en el idioma "it"

usuario1113000
fuente
2
el resultado de esa expresión son los nodos del título, no los nodos del libro
Caleth
2
Eso no es verdad. Devolverá los nodos del libro (estos dos puntos al final apuntan al nodo superior del nodo del título).
user1113000