Отримайте вузли, де дочірній вузол містить атрибут


116

Припустимо, у мене є такий 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>

Я хотів би зробити xpath, який повертає всі вузли книги, які мають титульний вузол з атрибутом мови "it".

Моя спроба виглядала приблизно так:

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

Але це не спрацювало. Я очікую повернути вузли:

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

Якісь підказки?


Що це за XPath реалізація?
Павло Мінаєв,

Відповіді:


175

Спробуйте

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

Це так:

  • отримати всі bookелементи
    • які мають хоча б одну title
      • який має атрибут lang
        • зі значенням "it"

Ви можете знайти це корисне - це стаття під назвою «XPath в п'яти пунктах» Рональд Bourret.

Але, чесно кажучи, //book[title[@lang='it']]і вищесказане повинно бути рівнозначним, якщо тільки у вашого двигуна XPath немає "проблем". Таким чином, це може бути щось у коді чи зразку XML, які ви нам не показуєте - наприклад, ваш зразок є фрагментом XML. Можливо, у кореневого елемента є область імен, і ви не враховуєте це у своєму запиті? І ви лише сказали нам, що це не працює, але ви не сказали, які результати отримали.


4
Як зробити те ж саме, якщо titleце не пряма дитина book, але десь глибше, і ми не знаємо, де саме? //book[/title/@lang = 'it']здається, не працює?
Мартін Конічек

5
Мартине, ти можеш використовувати //book evidence.//title/@lang = 'це']. Я вважаю, що фокус - це "". на початку стану.
Бруно Капоні

1
Дякуємо за посилання, відмінна стаття. Я використовую xPath роками, але це дійсно допомогло мені зрозуміти основну логіку!
swensor

57

Через роки, але корисним варіантом було б використання XPath Axes ( https://www.w3schools.com/xml/xpath_axes.asp ). Більш конкретно, ви шукаєте використовувати осі нащадків .

Я вважаю, що цей приклад зробив би свою справу

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

Це дозволяє вибрати всі bookелементи, які містять дочірній titleелемент (незалежно від того, наскільки глибоко він вкладений), що містять значення атрибута мови, рівне "it".

Я не можу точно сказати, чи відповідає ця відповідь 2009 року чи ні, оскільки я не на 100% впевнений, що XPath Axes існував у той час. Що я можу підтвердити, це те, що вони існують сьогодні, і я вважаю, що вони надзвичайно корисні в навігації XPath, і я впевнений, що ви також це зробите.


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

насправді еквівалентний

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

Я спробував це за допомогою vtd-xml, обидва вирази виплюнули один і той же результат ... який xpath-процесор ви використовували? Я здогадуюсь, що це питання відповідності. Нижче наведено код

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, що його питання відповідності та що синтаксис генерує той самий вузол. Подібний код у C # також працює.
Зак Бонхем,

-1: Містер Чжан, я намагався зробити вам послугу, видаливши код, не відповідний питанню. Це дозволило мені не звертати увагу на тебе, чого я зараз вважаю, що мушу. Зауважте, що жоден інший варіант відповіді не включав код для виклику виклику на запит.
Джон Сондерс

6
+1: Тому що я не можу зрозуміти, про що говорить пан Сондерс - жодна інша відповідь не додала жодного коду, і ця відповідь показує використаний код, щоб ми могли 1: перевірити його методи та 2: виконати тест самостійно. Код короткий і легкий для читання. Я не бачу проблеми.
DuckPuppy

4

Я думаю, що ваша власна пропозиція є правильною, однак xml не зовсім коректна. Якщо ви працюєте з//book[title[@lang='it']] на <root>[Your"XML"Here]</root>те безкоштовних онлайн - тестерів XPATH , таких як один тут буде знайти очікуваний результат.


2

Спробуйте використовувати цей вираз xPath:

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

Це повинно дати вам усі вузли книги в "it" lang


2
Результатом цього виразу є заголовкові вузли, а не книжкові вузли
Калет

2
Це не правда. Він поверне вузли книги (ці дві точки в кінці мають націлитись на верхній вузол заголовкового вузла).
користувач1113000
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.