XPath contiene (text (), 'some string') non funziona se usato con un nodo con più di un nodo secondario Text


259

Ho un piccolo problema con Xpath contiene con dom4j ...

Diciamo che il mio XML è

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

Diciamo che voglio trovare tutti i nodi che hanno ABC nel testo dato l'elemento radice ...

Quindi lo xpath che avrei dovuto scrivere sarebbe

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

Tuttavia, questo non è ciò che Dom4j restituisce ... è questo un problema dom4j o la mia comprensione di come funziona xpath. poiché quella query restituisce solo l'elemento Street e non l'elemento Comment.

Il DOM rende l'elemento Comment un elemento composito con quattro tag due

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

Suppongo che la query debba comunque restituire l'elemento poiché dovrebbe trovare l'elemento ed eseguirlo contiene ma non ...

la seguente query restituisce l'elemento ma restituisce molto più dell'elemento, restituisce anche gli elementi padre ... il che è indesiderabile per il problema ...

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

Qualcuno conosce la query xpath che restituirebbe solo gli elementi <Street/>e <Comment/>?


Per quanto ne so, //*[contains(text(),'ABC')]restituisce solo l' <Street>elemento. Non restituisce antenati di <Street>o <Comment>.
Ken Bloom,

Risposte:


707

Il <Comment>tag contiene due nodi di testo e due <br>nodi come elementi secondari.

La tua espressione xpath era

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

Per scomporlo,

  1. * è un selettore che corrisponde a qualsiasi elemento (ovvero tag) - restituisce un set di nodi.
  2. La []sono un condizionale che opera su ogni singolo nodo nel set di nodi. Corrisponde se uno dei singoli nodi su cui opera corrisponde alle condizioni all'interno delle parentesi.
  3. text()è un selettore che corrisponde a tutti i nodi di testo che sono figli del nodo di contesto: restituisce un set di nodi.
  4. containsè una funzione che opera su una stringa. Se viene passato un set di nodi, il set di nodi viene convertito in una stringa restituendo il valore di stringa del nodo nel set di nodi che è il primo nell'ordine del documento . Quindi, può corrispondere solo al primo nodo di testo nel tuo <Comment>elemento, vale a dire BLAH BLAH BLAH. Dal momento che non corrisponde, non si ottiene un <Comment>nei risultati.

Devi cambiarlo in

//*[text()[contains(.,'ABC')]]
  1. * è un selettore che corrisponde a qualsiasi elemento (ovvero tag) - restituisce un set di nodi.
  2. Gli esterni []sono un condizionale che opera su ogni singolo nodo in quel set di nodi - qui opera su ogni elemento del documento.
  3. text()è un selettore che corrisponde a tutti i nodi di testo che sono figli del nodo di contesto: restituisce un set di nodi.
  4. Gli interni []sono un condizionale che opera su ciascun nodo in quel set di nodi - qui ogni singolo nodo di testo. Ogni singolo nodo di testo è il punto di partenza per qualsiasi percorso tra parentesi e può anche essere indicato esplicitamente come .tra parentesi. Corrisponde se uno dei singoli nodi su cui opera corrisponde alle condizioni all'interno delle parentesi.
  5. containsè una funzione che opera su una stringa. Qui viene passato un singolo nodo di testo ( .). Poiché viene passato il secondo nodo di testo nel <Comment>tag singolarmente, vedrà la 'ABC'stringa e sarà in grado di abbinarla.

1
Impressionante sono un po 'un noob xpath, quindi fammi capire, text () è una funzione che prende l'espressione contiene (.,' ABC '), c'è una possibilità che tu possa spiegare, quindi non faccio questo cose stupide di nuovo;)
Mike Milkin

28
Ho modificato la mia risposta per fornire una lunga spiegazione. Non so molto di me stesso su XPath: ho solo sperimentato un po 'fino a quando non sono incappato in quella combinazione. Una volta che ho avuto una combinazione funzionante, ho fatto un'ipotesi su cosa stesse succedendo e ho guardato nello standard XPath per confermare ciò che pensavo stesse succedendo e scrivere la spiegazione.
Ken Bloom,

2
Come potreste rendere questo una ricerca senza distinzione tra maiuscole e minuscole?
Zack,

@Zack: per favore fai di questo una nuova domanda.
user1129682

1
So che questo è un vecchio thread, ma chiunque può commentare se c'è una differenza fondamentale, preferibilmente con alcuni semplici casi di test tra la risposta data da Ken Bloom e //*[contains(., 'ABC')]. Avevo sempre usato il modello dato da Mike Milkin, pensando che fosse più appropriato, ma semplicemente fare containsil contesto attuale sembra essere ciò che voglio più spesso.
knickum,

7

[contains(text(),'')]restituisce solo vero o falso. Non restituirà alcun risultato elemento.


questo non funzionerà se avessi '' o '' come possiamo tagliare?
Shareef

contains(text(),'JB-')non è lavoro! conatainsaccetta due stringhe come argomenti - contains(**string**, **string**)! text () non è una stringa , è una funzione!
AtachiShadow il

6

Il documento XML:

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

L'espressione XPath:

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

//*corrisponde a qualsiasi elemento discendente del nodo radice . Cioè, qualsiasi elemento tranne il nodo principale.

[...]è un predicato , filtra il set di nodi. Restituisce nodi per i quali ...è true:

Un predicato filtra un set di nodi [...] per produrre un nuovo set di nodi. Per ogni nodo nel set di nodi da filtrare, PredicateExpr viene valutato [...]; se PredicateExpr restituisce true per quel nodo, il nodo è incluso nel nuovo set di nodi; in caso contrario, non è incluso.

contains('haystack', 'needle')ritorna truese haystack contiene needle :

Funzione: booleano contiene (stringa, stringa)

La funzione contiene restituisce true se la prima stringa di argomenti contiene la seconda stringa di argomenti e in caso contrario restituisce false.

Ma contains()prende una stringa come primo parametro. Ed è passato nodi. Per far ciò, ogni nodo o set di nodi passato come primo parametro viene convertito in una stringa dalla string()funzione:

Un argomento viene convertito in tipo stringa come se chiamando la funzione stringa.

string()restituisce la funzione string-valuedel primo nodo :

Un set di nodi viene convertito in una stringa restituendo il valore di stringa del nodo nel set di nodi che è il primo nell'ordine del documento. Se il set di nodi è vuoto, viene restituita una stringa vuota.

string-valuedi un nodo elemento :

Il valore di stringa di un nodo elemento è la concatenazione dei valori di stringa di tutti i discendenti del nodo di testo del nodo elemento nell'ordine del documento.

string-valuedi un nodo di testo :

Il valore di stringa di un nodo di testo sono i dati dei caratteri.

Quindi, in sostanza, string-valuetutto il testo è contenuto in un nodo (concatenazione di tutti i nodi di testo discendenti).

text() è un test di nodo che corrisponde a qualsiasi nodo di testo:

Il testo di prova del nodo () è vero per qualsiasi nodo di testo. Ad esempio, child :: text () selezionerà i figli del nodo di testo del nodo di contesto.

Detto questo, //*[contains(text(), 'ABC')]corrisponde a qualsiasi elemento (tranne il nodo principale), il cui primo nodo di testo contiene ABC. Since text()restituisce un set di nodi che contiene tutti i nodi di testo figlio del nodo di contesto (rispetto al quale viene valutata un'espressione). Ma contains()prende solo il primo. Quindi per il documento sopra il percorso corrisponde Streetall'elemento.

L'espressione seguente //*[text()[contains(., 'ABC')]]corrisponde a qualsiasi elemento (tranne il nodo principale), che ha almeno un nodo di testo figlio, che contiene ABC. .rappresenta il nodo di contesto. In questo caso, è un nodo di testo figlio di qualsiasi elemento tranne il nodo principale. Quindi per il documento sopra il percorso corrisponde a Street, e agli Commentelementi.

Ora, quindi, //*[contains(., 'ABC')]corrisponde a qualsiasi elemento (tranne il nodo radice) che contiene ABC(nella concatenazione dei nodi di testo discendente). Per il documento sopra corrisponde agli elementi Home, the Addr, the Streete Comment. Come tale, //*[contains(., 'BLAH ABC')]partite i Home, i Addr, e gli Commentelementi.


0

Mi ci è voluto un po 'di tempo, ma alla fine ho capito. Xpath personalizzato che contiene alcuni testi di seguito ha funzionato perfettamente per me.

//a[contains(text(),'JB-')]

2
contains(text(),'JB-')non è lavoro! conatainsaccetta due stringhe come argomenti - contains(**string**, **string**)! text () non è una stringa , è una funzione!
AtachiShadow il

0

La risposta accettata restituirà anche tutti i nodi principali. Per ottenere solo i nodi effettivi con ABC anche se la stringa è successiva
:

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

0
//*[text()='ABC'] 

ritorna

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>

3
Quando aggiungi una risposta a una domanda di nove anni con cinque risposte esistenti, è molto importante sottolineare quale nuovo aspetto unico della domanda affronta la tua risposta.
Jason Aller,

La risposta che ho pubblicato è stata molto semplice. Così pensato come condividere, che può aiutare i principianti come me.
user3520544
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.