Jak tutaj używać XPath zawiera ()?

142

Próbuję się nauczyć XPath. Spojrzałem na inne contains()przykłady tutaj, ale nie ma nic, co używa operatora AND . Nie mogę tego uruchomić:

//ul[@class='featureList' and contains(li, 'Model')]

Na:

...
<ul class="featureList">

<li><b>Type:</b> Clip Fan</li><li><b>Feature:</b> Air Moved: 65 ft.
    Amps: 1.1
    Clip: Grips any surface up to 1.63"
    Plug: 3 prong grounded plug on heavy duty model
    Usage: Garage, Workshop, Dorm, Work-out room, Deck, Office & more.</li><li><b>Speed Setting:</b> 2 speeds</li><li><b>Color:</b> Black</li><li><b>Power Consumption:</b> 62 W</li><li><b>Height:</b> 14.5"</li><li><b>Width:</b> Grill Diameter: 9.5"</li><li><b>Length:</b> 11.5"</li>

<li><b>Model #: </b>CR1-0081-06</li>
<li><b>Item #: </b>N82E16896817007</li>
<li><b>Return Policy: </b></li>
</ul>
...
ryeguy
źródło
to działa dla mnie, przetestowałem to na whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm
mihi

Odpowiedzi:

199

Szukasz tylko przy pierwszym lidziecku w zapytaniu masz zamiast szukać jakiegokolwiek lielementu podrzędnego, które mogą zawierać tekst 'Model'. Potrzebujesz zapytania, takiego jak następujące:

//ul[@class='featureList' and ./li[contains(.,'Model')]]

To zapytanie daje elementy, które mają działanie classz featureListjednym lub większą liczbą lidzieci, które zawierają tekst 'Model'.

Jeff Yates
źródło
13
+1 - „./” jest nieco mylące - sugeruje, że po opuszczeniu go pod uwagę brane byłoby wszystko inne niż bieżący węzeł, ale w rzeczywistości jest to zbędne: "// ul [@ class = ' featureList 'i li [zawiera (.,' Model ')]] ”to to samo.
Tomalak
4
Tak, po prostu byłem konkretny. Całkiem możliwe, że zbyt szczegółowe.
Jeff Yates,
Jeśli nie ma liz Modelin ul, andstan się nie powiedzie. Zatem andwarunek powraca falsena pusty zbiór, czy jest poprawny?
damluar,
58

Dałem już +1 rozwiązaniu Jeffa Yatesa.

Oto krótkie wyjaśnienie, dlaczego Twoje podejście nie działa. To:

// ul [@ class = 'featureList' i zawiera (li, 'Model')]

napotyka ograniczenie contains()funkcji (lub dowolną inną funkcję tekstową w XPath, jeśli o to chodzi).

Pierwszy argument ma być łańcuchem. Jeśli podasz mu listę węzłów (dając mu to " li"), konwersja na łańcuch musi mieć miejsce. Ale ta konwersja jest wykonywana tylko dla pierwszego węzła na liście.

W Twoim przypadku pierwszy węzeł na liście jest <li><b>Type:</b> Clip Fan</li>(konwertowany na ciąg: „ Type: Clip Fan”), co oznacza, że:

// ul [@ class = 'featureList' i zawiera (li, 'Type')]

faktycznie wybierze węzeł!

Tomalak
źródło
1
miło, trudno było zrozumieć, dlaczego zapytania takie jak: ".//td[contains(.//*,'something ')]" działają tylko do głębokości 1. Wymyśliłem, jak to działa, ale nie udało mi się jestem pewien, jak powyższe w ogóle działało. Właściwie potrzebowałem „.//td[.//*[contains(.,'something ')]]”
JonnyRaa,
11

To nowa odpowiedź na stare pytanie dotyczące powszechnego nieporozumienia dotyczącego contains()XPath ...

Podsumowanie: contains()oznacza, że zawiera podciąg , a nie zawiera węzła .

Szczegółowe wyjaśnienie

Ten XPath jest często błędnie interpretowany:

//ul[contains(li, 'Model')]

Błędna interpretacja: wybrać te ulelementy, które zawierają takie lielementy ze Modelw nim.

To jest złe, ponieważ

  1. contains(x,y)oczekuje, xże będzie ciągiem, a
  2. reguła XPath dotycząca konwersji wielu elementów na ciąg jest następująca :

    Zestaw węzłów jest konwertowany na łańcuch przez zwrócenie wartości ciągu węzła w zestawie węzłów, który jest pierwszy w kolejności dokumentu . Jeśli zestaw węzłów jest pusty, zwracany jest pusty ciąg.

Prawo interpretacji: wybrać te ulelementy, których pierwsze li dziecko ma String wartość , która zawiera się Modelpodciąg.

Przykłady

XML

<r>
  <ul id="one">
    <li>Model A</li>
    <li>Foo</li>
  </ul>
  <ul id="two">
    <li>Foo</li>
    <li>Model A</li>
  </ul>
</r> 

XPaths

  • //ul[contains(li, 'Model')]wybiera one ulelement.

    Uwaga:two ul element nie jest wybrany, ponieważ ciąg wartości pierwszego lidzieckiem two uljest Foo, który nie zawiera Modelpodciąg.

  • //ul[li[contains(.,'Model')]]wybiera elementy onei two ul.

    Uwaga: oba ulelementy są zaznaczone, ponieważ contains()są stosowane do każdego z nich liosobno. (W ten sposób unika się skomplikowanej reguły konwersji wielu elementów na ciąg znaków). Oba ulelementy mają element lipotomny, którego wartość ciągu zawiera Modelpodłańcuch - pozycja lielementu nie ma już znaczenia.

Zobacz też

kjhughes
źródło
-2
//ul[@class="featureList" and li//text()[contains(., "Model")]]
runrig
źródło
-5

Wklej mój containsprzykład tutaj:

//table[contains(@class, "EC_result")]/tbody
hahakubile
źródło
2
W kodzie OP nie ma wartości tableelementu ani EC_resultklasy. Ta odpowiedź nie ma tutaj sensu i powinna zostać usunięta.
kjhughes