XPath contém (text (), 'alguma string') não funciona quando usado com nó com mais de um subnó de texto

259

Eu tenho um pequeno problema com o Xpath contém com dom4j ...

Vamos dizer que meu XML é

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

Vamos dizer que eu quero encontrar todos os nós que têm ABC no texto, dado o elemento raiz ...

Então o xpath que eu precisaria escrever seria

//*[contains(text(),'ABC')]

No entanto, não é isso que o Dom4j retorna .... esse é um problema do dom4j ou eu entendo como o xpath funciona. pois essa consulta retorna apenas o elemento Street e não o elemento Comment.

O DOM torna o elemento Comment um elemento composto com quatro tags dois

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

Eu assumiria que a consulta ainda deve retornar o elemento, uma vez que deve encontrar o elemento e executar contém nele, mas não ... ...

a consulta a seguir retorna o elemento, mas retorna muito mais do que apenas o elemento, retorna os elementos pai também ... o que é indesejável para o problema ...

//*[contains(text(),'ABC')]

Alguém conhece a consulta xpath que retornaria apenas os elementos <Street/>e <Comment/>?

Mike Milkin
fonte
Até onde eu sei, //*[contains(text(),'ABC')]retorna apenas o <Street>elemento. Não retorna nenhum ancestral de <Street>ou <Comment>.
Ken Bloom

Respostas:

707

A <Comment>tag contém dois nós de texto e dois <br>nós como filhos.

Sua expressão xpath foi

//*[contains(text(),'ABC')]

Para quebrar isso,

  1. * é um seletor que corresponde a qualquer elemento (ou seja, tag) - ele retorna um conjunto de nós.
  2. O []são uma condicional que opera em cada nó individual nesse conjunto nó. Corresponde se algum dos nós individuais em que opera corresponder às condições dentro dos colchetes.
  3. text()é um seletor que corresponde a todos os nós de texto filhos do nó de contexto - ele retorna um conjunto de nós.
  4. containsé uma função que opera em uma string. Se for passado um conjunto de nós, o conjunto de nós será convertido em uma sequência retornando o valor da sequência do nó no conjunto de nós que é o primeiro na ordem do documento . Portanto, ele pode corresponder apenas ao primeiro nó de texto no seu <Comment>elemento - ou seja BLAH BLAH BLAH. Como isso não corresponde, você não obtém <Comment>resultados nos resultados.

Você precisa alterar isso para

//*[text()[contains(.,'ABC')]]
  1. * é um seletor que corresponde a qualquer elemento (ou seja, tag) - ele retorna um conjunto de nós.
  2. O externo []é um condicional que opera em cada nó individual nesse conjunto de nós - aqui, ele opera em cada elemento do documento.
  3. text() é um seletor que corresponde a todos os nós de texto filhos do nó de contexto - ele retorna um conjunto de nós.
  4. O interior [] é um condicional que opera em cada nó nesse conjunto de nós - aqui cada nó de texto individual. Cada nó de texto individual é o ponto de partida para qualquer caminho entre parênteses e também pode ser referido explicitamente como .entre parênteses. Corresponde se algum dos nós individuais em que opera corresponder às condições dentro dos colchetes.
  5. containsé uma função que opera em uma string. Aqui é passado um nó de texto individual ( .). Como é passado o segundo nó de texto na <Comment>tag individualmente, ele verá a 'ABC'sequência e poderá correspondê-la.
Ken Bloom
fonte
1
Impressionante, eu sou um pouco parecido com um xpath noob, então deixe-me entender, text () é uma função que pega a expressão contém (., 'ABC'). Existe uma chance de você poder explicar para que eu não faça isso coisas estúpidas novamente;)
Mike Milkin
28
Editei minha resposta para fornecer uma longa explicação. Eu realmente não sei muito sobre XPath - apenas experimentei um pouco até me deparar com essa combinação. Depois de ter uma combinação de trabalho, adivinhei o que estava acontecendo e procurei no padrão XPath para confirmar o que eu pensava estar acontecendo e escreva a explicação.
Ken Bloom
2
Como você faria desta uma pesquisa sem distinção entre maiúsculas e minúsculas?
Zack
@ Zack: Por favor, faça desta uma nova pergunta.
User1129682
1
Sei que esse é um tópico antigo, mas alguém pode comentar se há uma diferença fundamental, de preferência com alguns casos de teste simples entre a resposta dada por Ken Bloom e //*[contains(., 'ABC')]. Eu sempre usei o padrão dado por Mike Milkin, pensando que era mais apropriado, mas apenas fazer containsno contexto atual parece realmente ser o que eu quero com mais frequência.
knickum
7

[contains(text(),'')]retorna apenas verdadeiro ou falso. Não retornará nenhum resultado de elemento.

Ratna
fonte
isso não vai funcionar se eu tivesse '' ou '' como podemos aparar?
shareef
contains(text(),'JB-')não é trabalho! conatainsusa duas strings como argumentos - contains(**string**, **string**)! text () não é string , é uma função!
AtachiShadow 27/03/19
6

O documento XML:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

A expressão XPath:

//*[contains(text(), 'ABC')]

//*corresponde a qualquer elemento descendente do nó raiz . Ou seja, qualquer elemento, exceto o nó raiz.

[...]é um predicado , filtra o conjunto de nós. Retorna nós para os quais ...é true:

Um predicado filtra um conjunto de nós [...] para produzir um novo conjunto de nós. Para cada nó no conjunto de nós a ser filtrado, o PredicateExpr é avaliado [...]; se PredicateExpr for avaliado como verdadeiro para esse nó, o nó será incluído no novo conjunto de nós; caso contrário, não está incluído.

contains('haystack', 'needle')retorna truese haystack contém needle :

Função: boolean contém (string, string)

A função contém retornará verdadeiro se a primeira sequência de argumentos contiver a segunda sequência de argumentos e, caso contrário, retornará falso.

Mas contains()usa uma string como seu primeiro parâmetro. E são nós passados. Para lidar com isso, todos os nós ou conjuntos de nós transmitidos como o primeiro parâmetro são convertidos em uma cadeia de caracteres pela string()função:

Um argumento é convertido no tipo string como se chamando a função string.

string()retornos string-valueda função do primeiro nó :

Um conjunto de nós é convertido em uma cadeia de caracteres retornando o valor da cadeia de nós no conjunto de nós que é o primeiro na ordem do documento. Se o conjunto de nós estiver vazio, uma sequência vazia será retornada.

string-valuede um nó do elemento :

O valor da sequência de um nó do elemento é a concatenação dos valores da sequência de todos os descendentes do nó de texto do nó do elemento na ordem do documento.

string-valuede um nó de texto :

O valor da sequência de um nó de texto são os dados dos caracteres.

Portanto, basicamente, string-valuetodo o texto está contido em um nó (concatenação de todos os nós de texto descendentes).

text() é um teste de nó que corresponde a qualquer nó de texto:

O texto de teste do nó () é verdadeiro para qualquer nó de texto. Por exemplo, child :: text () selecionará os filhos do nó de texto do nó de contexto.

Dito isto, //*[contains(text(), 'ABC')]corresponde a qualquer elemento (exceto o nó raiz), cujo primeiro nó de texto contém ABC. Desde text()retorna um conjunto de nós que contém todos os nós de texto filho do nó de contexto (em relação aos quais uma expressão é avaliada). Mas contains()leva apenas o primeiro. Portanto, para o documento acima, o caminho corresponde ao Streetelemento.

A expressão a seguir //*[text()[contains(., 'ABC')]]corresponde a qualquer elemento (exceto o nó raiz), que possui pelo menos um nó de texto filho, que contém ABC. .representa o nó de contexto. Nesse caso, é um nó de texto filho de qualquer elemento, exceto o nó raiz. Portanto, para o documento acima, o caminho corresponde Streetaos Commentelementos e.

Agora, //*[contains(., 'ABC')]corresponde a qualquer elemento (exceto o nó raiz) que contém ABC(na concatenação dos nós de texto descendentes). Para o documento acima, ele corresponde aos elementos the Home, the Addr, the Streete Comment. Como tal, //*[contains(., 'BLAH ABC')]coincide com os Home, os Addr, e os Commentelementos.

x-yuri
fonte
0

Demorei um pouco, mas finalmente descobri. O xpath personalizado que contém algum texto abaixo funcionou perfeitamente para mim.

//a[contains(text(),'JB-')]
zagoo2000
fonte
2
contains(text(),'JB-')não é trabalho! conatainsusa duas strings como argumentos - contains(**string**, **string**)! text () não é string , é uma função!
AtachiShadow 27/03/19
0

A resposta aceita retornará todos os nós pais também. Para obter apenas os nós reais com ABC, mesmo que a sequência seja posterior
:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Roger Veciana
fonte
0
//*[text()='ABC'] 

retorna

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
user3520544
fonte
3
Ao adicionar uma resposta a uma pergunta de nove anos com cinco respostas existentes, é muito importante apontar o novo aspecto único da pergunta que sua resposta aborda.
Jason Aller
A resposta que publiquei foi muito simples. Então, pensei em compartilhar, o que pode ajudar iniciantes como eu.
user3520544 20/06