XPath zawiera (text (), „jakiś ciąg”) nie działa, gdy jest używany z węzłem z więcej niż jednym podwęzłem Text

258

Mam mały problem z Xpath zawiera dom4j ...

Powiedzmy, że mój XML to

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

Powiedzmy, że chcę znaleźć wszystkie węzły, które mają ABC w tekście, biorąc pod uwagę element główny ...

Więc xpath, którą musiałbym napisać, to

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

Jednak nie to zwraca Dom4j .... to jest problem z dom4j lub moje zrozumienie, jak działa xpath. ponieważ to zapytanie zwraca tylko element Street, a nie element Comment.

DOM sprawia, że ​​element komentarza jest elementem złożonym z czterema znacznikami dwoma

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

Zakładam, że zapytanie powinno nadal zwracać element, ponieważ powinno ono znaleźć element i uruchomić na nim, ale nie ...

następujące zapytanie zwraca element, ale zwraca znacznie więcej niż tylko element, zwraca również elementy nadrzędne ... co jest niepożądane dla problemu ...

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

Czy ktoś zna zapytanie xpath, które zwróci tylko elementy <Street/>i <Comment/>?

Mike Milkin
źródło
O ile wiem, //*[contains(text(),'ABC')]zwraca tylko <Street>element. Nie zwraca żadnych przodków <Street>ani <Comment>.
Ken Bloom

Odpowiedzi:

706

<Comment>Tag zawiera dwa węzły tekstowe i dwa <br>węzły jak dzieci.

Twoje wyrażenie xpath było

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

Aby to rozbić,

  1. * jest selektorem pasującym do dowolnego elementu (tj. znacznika) - zwraca zestaw węzłów.
  2. []Są warunkowe, które działa na każdym węźle w tym zbiorze węzłów. Dopasowuje, jeśli którykolwiek z poszczególnych węzłów, na których działa, odpowiada warunkom w nawiasach.
  3. text()to selektor pasujący do wszystkich węzłów tekstowych, które są potomkami węzła kontekstowego - zwraca zestaw węzłów.
  4. containsto funkcja działająca na łańcuchu. Jeśli zostanie przekazany zestaw węzłów, zestaw węzłów jest konwertowany na ciąg znaków, zwracając wartość ciągu węzła w zestawie węzłów, który jest najpierw w kolejności dokumentów . Dlatego może pasować tylko do pierwszego węzła tekstowego w <Comment>elemencie - mianowicie BLAH BLAH BLAH. Ponieważ to się nie zgadza, wyniki nie są wyświetlane <Comment>.

Musisz to zmienić na

//*[text()[contains(.,'ABC')]]
  1. * jest selektorem pasującym do dowolnego elementu (tj. znacznika) - zwraca zestaw węzłów.
  2. Zewnętrzne []to warunek, który działa na każdym pojedynczym węźle w tym zestawie węzłów - tutaj działa na każdym elemencie w dokumencie.
  3. text()to selektor pasujący do wszystkich węzłów tekstowych, które są potomkami węzła kontekstowego - zwraca zestaw węzłów.
  4. Wewnętrzne []to warunek, który działa na każdym węźle w tym zestawie węzłów - tutaj każdy pojedynczy węzeł tekstowy. Każdy pojedynczy węzeł tekstowy jest punktem początkowym dowolnej ścieżki w nawiasach, a także może być jawnie określany .w nawiasach. Dopasowuje, jeśli którykolwiek z poszczególnych węzłów, na których działa, odpowiada warunkom w nawiasach.
  5. containsto funkcja działająca na łańcuchu. Tutaj przekazywany jest pojedynczy węzeł tekstowy ( .). Ponieważ jest on przekazywany <Comment>osobno do drugiego węzła tekstowego w znaczniku, zobaczy 'ABC'łańcuch i będzie mógł go dopasować.
Ken Bloom
źródło
1
Awesome im trochę xpath noob, więc pozwól mi to zrozumieć, text () to funkcja, która przyjmuje wyrażenie zawiera (., 'ABC'), Czy jest szansa, że ​​możesz wyjaśnić, więc nie robię tego rodzaju znowu głupie rzeczy;)
Mike Milkin
28
Zredagowałem swoją odpowiedź, aby podać długie wyjaśnienie. Sam tak naprawdę nie wiem zbyt wiele o XPath - po prostu trochę eksperymentowałem, aż natknąłem się na tę kombinację. Kiedy miałem już działającą kombinację, zgadywałem, co się dzieje i sprawdziłem w standardzie XPath, aby potwierdzić, co się dzieje, i napisałem wyjaśnienie.
Ken Bloom,
2
Jak sprawiłbyś, by wyszukiwanie nie uwzględniało wielkości liter?
Zack,
@Zack: Zadaj nowe pytanie.
user1129682
1
Wiem, że to stary wątek, ale czy ktoś może komentować, czy istnieje zasadnicza różnica, najlepiej w przypadku kilku prostych przypadków testowych między odpowiedzią udzieloną przez Kena Blooma i //*[contains(., 'ABC')]. Zawsze korzystałem ze wzoru podanego przez Mike'a Milkina, myśląc, że containsbyłby bardziej odpowiedni, ale po prostu robienie tego w obecnym kontekście wydaje się faktycznie tym, czego chcę częściej.
knickum
7

[contains(text(),'')]zwraca tylko prawdę lub fałsz. Nie zwróci żadnych wyników elementów.

Ratna
źródło
to nie zadziała, gdybym miał „” lub „” jak możemy przycinać?
shareef
contains(text(),'JB-')to nie praca! conatainsprzyjmuje dwa ciągi jako argumenty - contains(**string**, **string**)! text () nie jest łańcuchem , jest funkcją!
AtachiShadow
6

Dokument XML:

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

Wyrażenie XPath:

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

//*pasuje do każdego potomka elementu z węzła głównego . Oznacza to, że każdy element oprócz węzła głównego.

[...]jest predykatem , filtruje zestaw węzłów. Zwraca węzły, dla których ...jest true:

Predykat filtruje zestaw węzłów [...], aby utworzyć nowy zestaw węzłów. Dla każdego węzła w zestawie węzłów, który ma być filtrowany, PredicateExpr jest analizowany [...]; jeśli PredicateExpr ma wartość true dla tego węzła, węzeł jest zawarty w nowym zestawie węzłów; w przeciwnym razie nie zostanie uwzględnione.

contains('haystack', 'needle')zwraca, truejeśli haystack zawiera needle :

Funkcja: boolean zawiera (ciąg, ciąg)

Funkcja zawiera zwraca true, jeśli pierwszy ciąg argumentu zawiera drugi ciąg argumentu, a w przeciwnym razie zwraca false.

Ale contains()jako pierwszy parametr przyjmuje ciąg. I to przeszedł węzły. Aby sobie z tym poradzić, każdy węzeł lub zestaw węzłów przekazany jako pierwszy parametr jest konwertowany na ciąg przez string()funkcję:

Argument jest konwertowany na ciąg znaków, tak jakby wywoływał funkcję ciągu.

string()funkcja zwraca string-valuez pierwszego węzła :

Zestaw węzłów jest konwertowany na ciąg znaków, zwracając wartość ciągu węzła w zestawie węzłów, który jest najpierw w kolejności dokumentów. Jeśli zestaw węzłów jest pusty, zwracany jest pusty ciąg.

string-valueo węzeł elementu :

Wartość ciągu węzła elementu jest konkatenacją wartości ciągu wszystkich potomków węzła tekstowego węzła elementu w kolejności dokumentów.

string-valuez węzła tekstowego :

Wartość ciągu tekstowego węzła to dane znakowe.

Zasadniczo string-valuejest to cały tekst zawarty w węźle (konkatenacja wszystkich potomnych węzłów tekstowych).

text() to test węzła pasujący do dowolnego węzła tekstowego:

Test tekstowy węzła () jest prawdziwy dla każdego węzła tekstowego. Na przykład child :: text () wybierze dzieci węzła tekstowego węzła kontekstu.

Powiedziawszy to, //*[contains(text(), 'ABC')]pasuje do dowolnego elementu (oprócz węzła głównego), którego pierwszy węzeł tekstowy zawiera ABC. Ponieważ text()zwraca zestaw węzłów, który zawiera wszystkie potomne węzły tekstowe węzła kontekstu (w odniesieniu do którego oceniane jest wyrażenie). Ale contains()bierze tylko pierwszy. Tak więc dla dokumentu powyżej ścieżka pasuje do Streetelementu.

Poniższe wyrażenie //*[text()[contains(., 'ABC')]]pasuje do dowolnego elementu (oprócz węzła głównego), który zawiera co najmniej jeden potomny węzeł tekstowy, który zawieraABC . .reprezentuje węzeł kontekstu. W tym przypadku jest to potomny węzeł tekstowy dowolnego elementu oprócz węzła głównego. Tak więc dla dokumentu powyżej ścieżka pasuje Streetdo Commentelementów i.

Teraz //*[contains(., 'ABC')]dopasowuje dowolny element (oprócz węzła głównego), który zawiera ABC(w konkatenacji potomnych węzłów tekstowych). Na dokumencie powyżej pasuje do Home, na Addr, na Street, a Commentelementy. Jako takie, //*[contains(., 'BLAH ABC')]pasuje do HomeZ Addr, a Commentelementy.

x-yuri
źródło
0

Zajęło mi to trochę czasu, ale w końcu się zorientowałem. Niestandardowa ścieżka xpath zawierająca tekst poniżej działała dla mnie idealnie.

//a[contains(text(),'JB-')]
zagoo2000
źródło
2
contains(text(),'JB-')to nie praca! conatainsprzyjmuje dwa ciągi jako argumenty - contains(**string**, **string**)! text () nie jest łańcuchem , jest funkcją!
AtachiShadow
0

Zaakceptowana odpowiedź zwróci również wszystkie węzły nadrzędne. Aby uzyskać tylko rzeczywiste węzły z ABC, nawet jeśli ciąg jest po
:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Roger Veciana
źródło
0
//*[text()='ABC'] 

zwroty

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
użytkownik3520544
źródło
3
Dodając odpowiedź do dziewięcioletniego pytania z pięcioma istniejącymi odpowiedziami, bardzo ważne jest wskazanie, jaki wyjątkowy nowy aspekt pytania dotyczy odpowiedzi.
Jason Aller
Odpowiedź, którą opublikowałem, była bardzo prosta. Pomyślałem więc o dzieleniu się, co może pomóc początkującym, takim jak ja.
user3520544