Как использовать здесь XPath contains ()?

143

Пытаюсь изучить XPath. Я просмотрел другие contains()примеры здесь, но ничего, что использует оператор AND . Я не могу заставить это работать:

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

На:

...
<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>
...
ржаной
источник
это работает для меня, я тестировал его на whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm
mihi

Ответы:

203

Вы смотрите только на первый liребенок в запросе у вас есть вместо того , чтобы искать для любого liдочернего элемента , который может содержать текст, 'Model'. Вам нужен следующий запрос:

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

Этот запрос даст вам элементы , которые имеют classот featureListодной или нескольких liдетей , которые содержат текст, 'Model'.

Джефф Йейтс
источник
13
+1 - "./" немного вводит в заблуждение - он предполагает, что все, кроме текущего узла, будет учтено, когда вы его опустите, но на самом деле это избыточно: "// ul [@ class = ' featureList 'и li [contains (.,' Model ')]] "- это одно и то же.
Tomalak
4
Ага, я просто был конкретен. Вполне возможно, излишне конкретный.
Джефф Йейтс,
Если нет liwith Modelin ul, то andусловие не выполняется . Итак, andусловие возвращается falseна пустой набор, это правильно?
Константин Милютин
59

Я уже дал свой +1 к решению Джеффа Йейтса.

Вот краткое объяснение, почему ваш подход не работает. Этот:

// ul [@ class = 'featureList' и содержит (li, 'Model')]

обнаруживает ограничение contains()функции (или любой другой строковой функции в XPath, если на то пошло).

Первый аргумент должен быть строкой. Если вы скармливаете ему список узлов (это liделает " "), то должно произойти преобразование в строку. Но это преобразование выполняется только для первого узла в списке.

В вашем случае первый узел в списке <li><b>Type:</b> Clip Fan</li>(преобразован в строку: " Type: Clip Fan"), что означает, что это:

// ul [@ class = 'featureList' и содержит (li, 'Type')]

фактически выбрал бы узел!

Томалак
источник
1
Хорошенький изо всех сил пытался понять, почему запросы вроде: ".//td[contains(.//*,'something ')]" работают только до глубины 1. Я понял, как заставить его работать, но не уверен, как это вообще работает. На самом деле мне было нужно ".//td[.//*[contains(.,'something ')]]"
JonnyRaa
12

Это новый ответ на старый вопрос о распространенном заблуждении о contains()XPath ...

Резюме: contains()означает, что содержит подстроку , а не узел .

Детальное объяснение

Этот XPath часто неверно интерпретируют:

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

Неправильная интерпретация: выберите те ulэлементы, которые содержат в себе liэлемент с Model.

Это неправильно, потому что

  1. contains(x,y)ожидает xбыть строкой, и
  2. Правило XPath для преобразования нескольких элементов в строку таково :

    Набор узлов преобразуется в строку путем возврата строкового значения узла в наборе узлов, который является первым в порядке документа . Если набор узлов пуст, возвращается пустая строка.

Право интерпретации: Выберите те ulэлементы, первый li ребенок имеет строковое значение , что содержит в Modelподстроку.

Примеры

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')]выбирает one ulэлемент.

    Примечание:two ul элемент не выбран , потому что строка-значение первого liребенка из two ulэто Foo, который не содержит Modelподстроку.

  • //ul[li[contains(.,'Model')]]выбирает oneи two ulэлементы.

    Примечание: оба ulэлемента выбраны, поскольку contains()применяется к каждому liиндивидуально. (Таким образом, можно избежать хитрого правила преобразования нескольких элементов в строку.) У обоих ulэлементов есть liдочерний элемент , строковое значение которого содержит Modelподстроку - позиция liэлемента больше не имеет значения.

Смотрите также

Kjhughes
источник
-2
//ul[@class="featureList" and li//text()[contains(., "Model")]]
Runrig
источник
-5

Вставьте мой containsпример сюда:

//table[contains(@class, "EC_result")]/tbody
hahakubile
источник
2
В коде OP нет значения tableэлемента или EC_resultкласса. Этот ответ здесь не имеет смысла и должен быть удален.
kjhughes