Pobierz węzły, w których węzeł podrzędny zawiera atrybut

116

Załóżmy, że mam następujący kod 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>

Chciałbym utworzyć ścieżkę xpath, która zwraca wszystkie węzły książek, które mają węzeł tytułu z atrybutem języka „it”.

Moja próba wyglądała mniej więcej tak:

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

Ale to nie zadziałało. Spodziewam się odzyskania węzłów:

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

Jakieś wskazówki?

Uwe Keim
źródło
Co to za implementacja XPath?
Pavel Minaev,

Odpowiedzi:

175

Próbować

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

To brzmi:

  • zdobądź wszystkie bookelementy
    • które mają co najmniej jeden title
      • który ma atrybut lang
        • o wartości "it"

Może ci się to przydać - jest to artykuł zatytułowany „XPath w pięciu akapitach” autorstwa Ronalda Bourreta.

Ale szczerze mówiąc, //book[title[@lang='it']]powyższe powinno być równoważne, chyba że twój silnik XPath ma „problemy”. Więc może to być coś w kodzie lub przykładowym pliku XML, czego nam nie pokazujesz - na przykład Twoja próbka jest fragmentem XML. Czy to możliwe, że element główny ma przestrzeń nazw, a ty nie liczysz tego w zapytaniu? Powiedziałeś nam tylko, że to nie zadziałało, ale nie powiedziałeś nam, jakie wyniki uzyskałeś.

lavinio
źródło
4
Jak zrobić to samo, jeśli titlenie jest bezpośrednim dzieckiem book, ale gdzieś głębiej i nie wiemy, gdzie dokładnie? //book[/title/@lang = 'it']nie wydaje się działać?
Martin Konicek
5
Martin, możesz użyć //book[.//title/@lang = 'it']. Uważam, że sztuczka polega na „”. na początku stanu.
Bruno Caponi
1
Dzięki za link, świetny artykuł. Używam xPath od lat, ale to naprawdę pomogło mi zrozumieć podstawową logikę!
swensor
57

Wiele lat później, ale użyteczną opcją byłoby użycie osi XPath ( https://www.w3schools.com/xml/xpath_axes.asp ). Mówiąc dokładniej, chcesz użyć osi potomków .

Myślę, że ten przykład załatwi sprawę:

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

Pozwala to na wybranie wszystkich bookelementów, które zawierają titleelement potomny (niezależnie od tego, jak głęboko jest on zagnieżdżony), zawierający wartość atrybutu języka równą „it”.

Nie mogę z całą pewnością stwierdzić, czy ta odpowiedź dotyczy roku 2009, ponieważ nie jestem w 100% pewien, czy osie XPath istniały w tamtym czasie. Mogę potwierdzić, że istnieją one dzisiaj i stwierdziłem, że są niezwykle przydatne w nawigacji XPath i jestem pewien, że Ty też.

wes.hysell
źródło
12
//book[title[@lang='it']]

jest faktycznie odpowiednikiem

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

Wypróbowałem to przy użyciu vtd-xml, oba wyrażenia dają ten sam wynik ... jakiego silnika przetwarzania xpath użyłeś? Myślę, że ma to problem ze zgodnością Poniżej znajduje się kod

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-autor
źródło
+1, że jest to problem ze zgodnością i że składnia generuje ten sam zestaw węzłów. Podobny kod w C # również działa.
Zach Bonham
-1: Panie Zhang, próbowałem wyświadczyć panu przysługę, usuwając kod niezwiązany z pytaniem. Pozwoliło mi to nie głosować przeciwko tobie, co teraz czuję, że muszę. Zwróć uwagę, że żadna inna odpowiedź nie zawiera kodu do wywołania zapytania.
John Saunders
6
+1: Ponieważ nie mogę zrozumieć, o czym mówi pan Saunders - żadna inna odpowiedź nie dodała ŻADNEGO kodu, a ta odpowiedź pokazuje użyty kod, więc możemy 1: zweryfikować jego metody i 2: przeprowadzić jego test samodzielnie. Kod jest krótki i łatwy do odczytania. Nie widzę problemu.
DuckPuppy
4

Myślę, że twoja własna sugestia jest poprawna, jednak XML nie jest do końca poprawny. Jeśli korzystasz z wersji //book[title[@lang='it']]on, <root>[Your"XML"Here]</root>bezpłatni testerzy xPath online, tacy jak tutaj , znajdą oczekiwany wynik.

Joakim Byg
źródło
2

Spróbuj użyć tego wyrażenia xPath:

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

To powinno dać ci wszystkie węzły książek w języku „to”

user1113000
źródło
2
wynikiem tego wyrażenia są węzły tytułowe, a nie węzły książki
Caleth
2
To nie jest prawda. Zwróci węzły książki (te dwie kropki na końcu mają wskazywać na górny węzeł węzła tytułowego).
user1113000