Получить узлы, в которых дочерний узел содержит атрибут

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 в пяти абзацах» .

Но, честно говоря, //book[title[@lang='it']]и вышесказанное должно быть эквивалентным, если только ваш движок XPath не имеет "проблем". Таким образом, это может быть что-то в коде или образце XML, который вы нам не показываете - например, ваш образец представляет собой фрагмент XML. Может быть, у корневого элемента есть пространство имен, и вы не учитываете это в своем запросе? И вы только сказали нам, что это не сработало, но вы не сказали нам, какие результаты вы получили.

Лавинио
источник
4
Как сделать то же самое, если titleоно не является прямым потомком book, а где-то глубже и мы не знаем где именно? //book[/title/@lang = 'it']не работает?
Мартин Коничек
5
Мартин, вы можете использовать //book[.//title/@lang = 'it']. Я считаю, что все дело в "." в начале условия.
Бруно Капони
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 существовали в то время. Что я могу подтвердить, так это то, что они существуют сегодня, и я считаю, что они чрезвычайно полезны в навигации XPath, и я уверен, что вы тоже.

wes.hysell
источник
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 ");*/

      }
  }
}
vtd-xml-author
источник
+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"

user1113000
источник
2
результатом этого выражения являются узлы заголовка, а не узлы книги
Калет
2
Это неправда. Он вернет узлы книги (эти две точки в конце должны быть направлены на верхний узел узла заголовка).
user1113000 03