XPath contient (text (), 'some string') ne fonctionne pas lorsqu'il est utilisé avec un nœud avec plusieurs sous-nœuds Text

259

J'ai un petit problème avec Xpath contient avec dom4j ...

Disons que mon XML est

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

Disons que je veux trouver tous les nœuds qui ont ABC dans le texte étant donné l'élément racine ...

Ainsi, le xpath que j'aurais eu besoin d'écrire serait

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

Cependant, ce n'est pas ce que Dom4j renvoie .... est-ce un problème dom4j ou ma compréhension du fonctionnement de xpath. puisque cette requête renvoie uniquement l'élément Street et non l'élément Comment.

Le DOM fait de l'élément Comment un élément composite avec quatre balises deux

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

Je suppose que la requête doit toujours renvoyer l'élément car elle doit trouver l'élément et exécuter contient dessus, mais il ne le fait pas ... ...

la requête suivante renvoie l'élément mais elle renvoie bien plus que l'élément, elle renvoie également les éléments parents ... ce qui n'est pas souhaitable pour le problème ...

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

Est-ce que quelqu'un connaît la requête xpath qui retournerait uniquement les éléments <Street/>et <Comment/>?

Mike Milkin
la source
Pour autant que je sache, //*[contains(text(),'ABC')]ne renvoie que l' <Street>élément. Il ne retourne aucun ancêtre de <Street>ou <Comment>.
Ken Bloom

Réponses:

707

La <Comment>balise contient deux nœuds de texte et deux <br>nœuds en tant qu'enfants.

Votre expression xpath était

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

Pour décomposer cela,

  1. * est un sélecteur qui correspond à n'importe quel élément (c'est-à-dire une balise) - il renvoie un ensemble de nœuds.
  2. Il []s'agit d'une conditionnelle qui fonctionne sur chaque nœud individuel de cet ensemble de nœuds. Il correspond si l'un des nœuds individuels sur lesquels il opère correspond aux conditions à l'intérieur des crochets.
  3. text()est un sélecteur qui correspond à tous les nœuds de texte qui sont des enfants du nœud de contexte - il renvoie un ensemble de nœuds.
  4. containsest une fonction qui opère sur une chaîne. Si un ensemble de nœuds lui est transmis, l'ensemble de nœuds est converti en chaîne en renvoyant la valeur de chaîne du nœud dans l'ensemble de nœuds qui est le premier dans l'ordre du document . Par conséquent, il ne peut correspondre qu'au premier nœud de texte de votre <Comment>élément, à savoir BLAH BLAH BLAH. Comme cela ne correspond pas, vous n'obtenez pas <Comment>de résultat dans vos résultats.

Vous devez changer cela en

//*[text()[contains(.,'ABC')]]
  1. * est un sélecteur qui correspond à n'importe quel élément (c'est-à-dire une balise) - il renvoie un ensemble de nœuds.
  2. L'extérieur []est une condition qui fonctionne sur chaque nœud individuel de cet ensemble de nœuds - ici, il fonctionne sur chaque élément du document.
  3. text()est un sélecteur qui correspond à tous les nœuds de texte qui sont des enfants du nœud de contexte - il renvoie un ensemble de nœuds.
  4. L'intérieur []est une condition qui opère sur chaque nœud de cet ensemble de nœuds - ici chaque nœud de texte individuel. Chaque nœud de texte individuel est le point de départ de tout chemin entre crochets et peut également être désigné explicitement comme .entre crochets. Il correspond si l'un des nœuds individuels sur lesquels il opère correspond aux conditions à l'intérieur des crochets.
  5. containsest une fonction qui opère sur une chaîne. Ici, il est passé un nœud de texte individuel ( .). Puisqu'il est passé le deuxième nœud de texte dans la <Comment>balise individuellement, il verra la 'ABC'chaîne et pourra la faire correspondre.
Ken Bloom
la source
1
Im génial un peu d'un noob xpath, alors laissez-moi obtenir ceci, text () est une fonction qui prend l'expression contient (., 'ABC'), Y a-t-il une chance que vous puissiez expliquer donc je ne fais pas ça un peu encore des trucs stupides;)
Mike Milkin
28
J'ai modifié ma réponse pour fournir une longue explication. Je ne sais pas vraiment grand-chose sur XPath moi-même - j'ai juste expérimenté un peu jusqu'à ce que je tombe sur cette combinaison. Une fois que j'ai eu une combinaison de travail, j'ai fait une supposition sur ce qui se passait et j'ai regardé dans la norme XPath pour confirmer ce que je pensais se passer et écrire l'explication.
Ken Bloom
2
Comment feriez-vous une recherche insensible à la casse?
Zack
@Zack: Veuillez en faire une nouvelle question.
user1129682
1
Je sais que c'est un vieux fil, mais quelqu'un peut-il commenter s'il y a une différence fondamentale, de préférence avec quelques cas de test simples entre la réponse donnée par Ken Bloom et //*[contains(., 'ABC')]. J'avais toujours utilisé le modèle donné par Mike Milkin, pensant qu'il était plus approprié, mais simplement faire containsdans le contexte actuel semble être ce que je veux le plus souvent.
knickum
7

[contains(text(),'')]renvoie uniquement vrai ou faux. Il ne renverra aucun résultat d'élément.

Ratna
la source
cela ne fonctionnera pas si j'avais '' ou '' comment pouvons-nous couper?
shareef
contains(text(),'JB-')ce n'est pas du travail! conatainsprend deux chaînes comme arguments - contains(**string**, **string**)! text () n'est pas une chaîne , est une fonction!
AtachiShadow
6

Le document XML:

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

L'expression XPath:

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

//*correspond à tout élément descendant du nœud racine . Autrement dit, tout élément, sauf le nœud racine.

[...]est un prédicat , il filtre l'ensemble de nœuds. Elle retourne les nœuds pour lesquels ...est true:

Un prédicat filtre un ensemble de nœuds [...] pour produire un nouvel ensemble de nœuds. Pour chaque nœud de l'ensemble de nœuds à filtrer, le PredicateExpr est évalué [...]; si PredicateExpr est évalué comme vrai pour ce nœud, le nœud est inclus dans le nouvel ensemble de nœuds; sinon, il n'est pas inclus.

contains('haystack', 'needle')renvoie truesi haystack contient needle :

Fonction: booléen contient (chaîne, chaîne)

La fonction contains renvoie true si la première chaîne d'arguments contient la deuxième chaîne d'arguments, et renvoie sinon false.

Mais contains()prend une chaîne comme premier paramètre. Et ce sont des nœuds passés. Pour gérer cela, chaque nœud ou ensemble de nœuds transmis en tant que premier paramètre est converti en chaîne par la string()fonction:

Un argument est converti en type chaîne comme si en appelant la fonction chaîne.

string()retourne la fonction string-valuedu premier nœud :

Un ensemble de nœuds est converti en chaîne en renvoyant la valeur de chaîne du nœud dans l'ensemble de nœuds qui est le premier dans l'ordre du document. Si l'ensemble de nœuds est vide, une chaîne vide est renvoyée.

string-valued'un nœud d'élément :

La valeur de chaîne d'un nœud d'élément est la concaténation des valeurs de chaîne de tous les descendants de nœud de texte du nœud d'élément dans l'ordre des documents.

string-valued'un nœud de texte :

La valeur de chaîne d'un nœud de texte correspond aux données de caractère.

Donc, fondamentalement, string-valuetout le texte est contenu dans un nœud (concaténation de tous les nœuds de texte descendants).

text() est un test de nœud qui correspond à n'importe quel nœud de texte:

Le texte de test du nœud () est vrai pour tout nœud de texte. Par exemple, child :: text () sélectionnera les enfants du nœud de texte du nœud de contexte.

Cela dit, //*[contains(text(), 'ABC')]correspond à tout élément (sauf le nœud racine), dont le premier nœud de texte contient ABC. Depuis text()retourne un ensemble de nœuds qui contient tous les nœuds de texte enfant du nœud de contexte (par rapport auxquels une expression est évaluée). Mais contains()ne prend que le premier. Donc, pour le document au-dessus du chemin correspond à l' Streetélément.

L'expression suivante //*[text()[contains(., 'ABC')]]correspond à tout élément (mais le nœud racine) qui contient au moins un nœud de texte enfant ABC. .représente le nœud de contexte. Dans ce cas, il s'agit d'un nœud de texte enfant de tout élément sauf le nœud racine. Donc, pour le document au-dessus, le chemin correspond Streetaux Commentéléments et.

Maintenant, //*[contains(., 'ABC')]correspond à tout élément (mais le nœud racine) qui contient ABC(dans la concaténation des nœuds de texte descendants). Pour le document ci-dessus, il correspond aux éléments Home, the Addr, the Streetet Comment. En tant que tel, //*[contains(., 'BLAH ABC')]correspond à la Home, les Addr, et les Commentéléments.

x-yuri
la source
0

Cela m'a pris un peu de temps mais j'ai finalement compris. Le xpath personnalisé qui contient du texte ci-dessous a parfaitement fonctionné pour moi.

//a[contains(text(),'JB-')]
zagoo2000
la source
2
contains(text(),'JB-')ce n'est pas du travail! conatainsprend deux chaînes comme arguments - contains(**string**, **string**)! text () n'est pas une chaîne , est une fonction!
AtachiShadow
0

La réponse acceptée renverra également tous les nœuds parents. Pour obtenir uniquement les nœuds réels avec ABC même si la chaîne est après
:

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

Retour

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
user3520544
la source
3
Lorsque vous ajoutez une réponse à une question de neuf ans avec cinq réponses existantes, il est très important de souligner le nouvel aspect unique de la question que votre réponse aborde.
Jason Aller
La réponse que j'ai postée était très simple. J'ai donc pensé partager, ce qui peut aider les débutants comme moi.
user3520544