Obter nós onde o nó filho contém um atributo

116

Suponha que eu tenha o seguinte 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>

Eu gostaria de fazer um xpath que recupere todos os nós do livro que têm um nó de título com um atributo de idioma de "isso".

Minha tentativa foi mais ou menos assim:

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

Mas isso não funcionou. Espero recuperar os nós:

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

Alguma dica?

Uwe Keim
fonte
Que implementação XPath é essa?
Pavel Minaev

Respostas:

175

Experimentar

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

Isso diz:

  • pegue todos os bookelementos
    • que tem pelo menos um title
      • que tem um atributo lang
        • com um valor de "it"

Você pode achar isso útil - é um artigo intitulado "XPath em cinco parágrafos", de Ronald Bourret.

Mas com toda a honestidade, //book[title[@lang='it']]e o acima deve ser equivalente, a menos que seu mecanismo XPath tenha "problemas". Portanto, pode ser algo no código ou amostra XML que você não está nos mostrando - por exemplo, sua amostra é um fragmento XML. Será que o elemento raiz tem um namespace, e você não está contando para isso em sua consulta? E você apenas nos disse que não funcionou, mas não nos disse quais resultados obteve.

lavinio
fonte
4
Como fazer o mesmo se titlenão é filho direto de book, mas em algum lugar mais profundo e não sabemos exatamente onde? //book[/title/@lang = 'it']não parece funcionar?
Martin Konicek
5
Martin, você pode usar //book[.//title/@lang = 'it']. Acredito que o truque seja o "." no início da condição.
Bruno Caponi
1
Obrigado pelo link, excelente artigo. Eu uso o xPath há anos, mas isso realmente me ajudou a entender a lógica subjacente!
swensor
57

Anos depois, mas uma opção útil seria utilizar os eixos XPath ( https://www.w3schools.com/xml/xpath_axes.asp ). Mais especificamente, você está procurando usar os eixos descendentes .

Acredito que este exemplo resolveria o problema:

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

Isso permite que você selecione todos os bookelementos que contêm um titleelemento filho (independentemente de quão profundo ele está aninhado) contendo o valor do atributo de idioma igual a 'it'.

Não posso dizer com certeza se esta resposta é ou não relevante para o ano de 2009, pois não estou 100% certo de que os eixos XPath existiam naquela época. O que posso confirmar é que eles existem hoje e os considero extremamente úteis na navegação XPath e tenho certeza de que você também o fará.

wes.hysell
fonte
12
//book[title[@lang='it']]

é realmente equivalente a

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

Eu tentei usando vtd-xml, ambas as expressões produziram o mesmo resultado ... qual mecanismo de processamento xpath você usou? Acho que tem problema de conformidade. Abaixo está o 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
fonte
1 que é um problema de conformidade e que a sintaxe gera o mesmo conjunto de nós. Código semelhante em C # também funciona.
Zach Bonham,
-1: Sr. Zhang, estava tentando lhe fazer um favor removendo o código não relevante para a pergunta. Isso me permitiu não votar contra você, o que agora sinto que devo fazer. Observe que nenhuma outra resposta incluiu código para chamar a consulta.
John Saunders
6
+1: Porque eu não consigo descobrir do que o Sr. Saunders está falando - nenhuma outra resposta adicionou QUALQUER código, e esta resposta mostra o código usado para que possamos 1: validar seus métodos e 2: realizar seu teste nós mesmos. O código é curto e fácil de ler. Eu não vejo o problema.
DuckPuppy
4

Eu acho que sua sugestão está correta, porém o xml não é muito válido. Se você estiver executando o //book[title[@lang='it']]on <root>[Your"XML"Here]</root>, os testadores xPath online gratuitos, como este aqui , encontrarão o resultado esperado.

Joakim Byg
fonte
2

Tente usar esta expressão xPath:

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

Isso deve dar a você todos os nós do livro na linguagem "it"

user1113000
fonte
2
o resultado dessa expressão são os nós do título, não os nós do livro
Caleth
2
Isso não é verdade. Ele retornará os nós do livro (esses dois pontos no final devem apontar para o nó superior do nó do título).
user1113000