Ottieni nodi in cui il nodo figlio contiene un attributo


116

Supponiamo di avere il seguente 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>

Vorrei fare un xpath che recuperi tutti i nodi del libro che hanno un nodo del titolo con un attributo di lingua "it".

Il mio tentativo assomigliava a questo:

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

Ma non ha funzionato. Mi aspetto di recuperare i nodi:

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

Eventuali suggerimenti?


Che implementazione XPath è questa?
Pavel Minaev,

Risposte:


175

Provare

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

Questo recita:

  • ottieni tutti gli bookelementi
    • che ne hanno almeno uno title
      • che ha un attributo lang
        • con un valore di "it"

Si possono trovare questo utile - è un articolo intitolato "XPath in cinque paragrafi" da Ronald Bourret.

Ma in tutta onestà, //book[title[@lang='it']]e quanto sopra dovrebbe essere equivalente, a meno che il tuo motore XPath non abbia "problemi". Quindi potrebbe essere qualcosa nel codice o nell'XML di esempio che non ci stai mostrando, ad esempio, il tuo esempio è un frammento XML. Potrebbe essere che l'elemento radice abbia uno spazio dei nomi e non lo stai contando nella tua query? E ci hai solo detto che non funzionava, ma non ci hai detto quali risultati hai ottenuto.


4
Come fare lo stesso se titlenon è un figlio diretto di book, ma da qualche parte più in profondità e non sappiamo esattamente dove? //book[/title/@lang = 'it']non sembra funzionare?
Martin Konicek

5
Martin, potresti usare //book[.//title/@lang = 'it']. Credo che il trucco sia il "." all'inizio della condizione.
Bruno Caponi

1
Grazie per il link, ottimo articolo. Uso xPath da anni ma questo mi ha davvero aiutato a capire la logica sottostante!
swensor

57

Anni dopo, ma un'opzione utile sarebbe quella di utilizzare XPath Axes ( https://www.w3schools.com/xml/xpath_axes.asp ). Più specificamente, stai cercando di utilizzare gli assi dei discendenti .

Credo che questo esempio farebbe il trucco:

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

Ciò consente di selezionare tutti gli bookelementi che contengono un titleelemento figlio (indipendentemente da quanto sia profondo nidificato) contenente il valore dell'attributo della lingua uguale a "it".

Non posso dire con certezza se questa risposta sia rilevante o meno per l'anno 2009 in quanto non sono certo al 100% che gli assi XPath esistessero in quel momento. Quello che posso confermare è che esistono oggi e li ho trovati estremamente utili nella navigazione XPath e sono sicuro che lo farai anche tu.


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

è effettivamente equivalente a

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

L'ho provato usando vtd-xml, entrambe le espressioni emettono lo stesso risultato ... quale motore di elaborazione xpath hai usato? Immagino che abbia un problema di conformità. Di seguito è riportato il codice

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 ");*/

      }
  }
}

+1 che è un problema di conformità e che la sintassi genera lo stesso nodeset. Funziona anche codice simile in C #.
Zach Bonham,

-1: Sig. Zhang, stavo cercando di farle un favore rimuovendo il codice non pertinente alla domanda. Mi ha permesso di non sottovalutarti, cosa che ora sento di dover fare. Notare che nessun'altra risposta includeva il codice per chiamare la query.
John Saunders,

6
+1: Perché non riesco a capire di cosa stia parlando il signor Saunders - nessun'altra risposta ha aggiunto QUALSIASI codice, e questa risposta mostra il codice usato così possiamo 1: convalidare i suoi metodi e 2: eseguire il suo test da soli. Il codice è breve e di facile lettura. Non vedo il problema.
DuckPuppy

4

Penso che il tuo suggerimento sia corretto, tuttavia l'xml non è del tutto valido. Se stai eseguendo //book[title[@lang='it']]on, <root>[Your"XML"Here]</root>i tester xPath online gratuiti come uno qui troveranno il risultato atteso.


2

Prova a usare questa espressione xPath:

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

Questo dovrebbe darti tutti i nodi del libro in lingua "it"


2
il risultato di questa espressione sono i nodi del titolo, non i nodi del libro
Caleth

2
Quello non è vero. Restituirà i nodi del libro (questi due punti all'estremità mirano al nodo superiore del nodo del titolo).
user1113000
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.