Я играл с различными XPath-запросами с помощью XPather (работает только в старых версиях firefox) и заметил разницу между результатами следующих запросов
Этот показывает некоторые результаты
//div[descendant::table/descendant::td[4]]
Здесь показан пустой список
//div[//table//td[4]]
Они отличаются из-за каких-то правил или это просто неправильное поведение конкретной реализации интерпретатора XPath? (Кажется, что XPather используется из движка FF, это просто отличный простой GUI для запросов).
В XPath 1.0 //
является сокращением для /descendant-or-self::node()/
, поэтому ваш первый путь будет /descendant-or-self::node()/div[descendant::table/descendant::td[4]]
, в то время как второй путь будет совсем другим /descendant-or-self::node()/div[/descendant-or-self::node()/table/descendant-or-self::node()/td[4]]
. Основная разница заключается в том, что в первом предикате вы ищете потомков относительно элемента div
, а во втором предикате вы ищете потомков от корневого узла /
(также называемого узлом документа).
Вам может понадобиться //div[.//table//td[4]]
, чтобы второе выражение пути было ближе к первому.
[edit]. Вот пример:
<html>
<body>
<div>
<table>
<tbody>
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>4</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
В этом примере путь //div[descendant::table/descendant::td[4]]
выбирает элемент div
, поскольку у него есть дочерний table
, у которого есть четвертый потомок td
.
Однако с //div[.//table//td[4]]
мы ищем //div[./descendant-or-self::node()/table/descendant-or-self::node()/td[4]]
, который является сокращением для //div[./descendant-or-self::node()/table/descendant-or-self::node()/child::td[4]]
и нет элемента, имеющего четвертый td
дочерний элемент.
Надеюсь, это объясняет разницу, если вы используете //div[.//table/descendant::td[4]]
, то вы должны получить тот же результат, что и с вашей оригинальной формой.
В документе W3C по XPath есть важное замечание:
NOTE: Путь расположения
//para[1]
означает не то же самое, что путь расположения/descendant::para[1]
. Последний выбирает первый элемент-потомокpara
; первый выбирает все элементы-потомкиpara
, которые являются первыми дочерними элементами para своих родителей.
Это означает, что двойная косая черта внутри пути является не только сокращением для /descendant-or-self::node()/
, но и отправной точкой для следующего уровня итерации XML-дерева, что подразумевает, что выражение шага справа от //
повторно выполняется на каждом потомке текущего контекстного узла.
Таким образом, точное значение предиката в этом пути
//div[ descendant::table/descendant::td[4] ]
это:
<table>
, нисходящих к текущему <div>
,<table>
построить последовательность всех элементов-потомков <td>
и объединить их в одну последовательность,Наконец, путь возвращает все элементы <div>
в документе, которые имеют по крайней мере четыре ячейки данных во всех своих вложенных таблицах. А поскольку в документе есть таблицы, которые имеют 4 ячейки или более (включая ячейки во вложенных таблицах, конечно), все выражение выбирает их соответствующих <div>
предков.
С другой стороны, предикат в
//div[ //table//td[4] ]
означает:
<table>
(точнее, проверить корневой узел и каждого потомка корня, если у него есть дочерний элемент <table>
),<td>
подэлемент (т.е. проверить, имеет ли таблица или любой из ее потомков как минимум четыре <td>
детища).Обратите внимание, что подвыражение предиката не зависит от узла контекста. Это глобальный путь, разрешающийся в некоторую последовательность узлов (возможно, пустую), поэтому булево значение предиката зависит только от структуры документа. Если оно истинно, то весь путь возвращает последовательность всех элементов <div>
в документе, иначе - пустую последовательность.
Наконец, предикат будет true, если в любой таблице есть элемент, имеющий 4 (как минимум) ячейки данных.
Насколько я могу судить, все строки <tr>
содержат две или три ячейки - нет элемента с 4 или более дочерними <td>
, поэтому подвыражение предиката возвращает пустую последовательность, предикат false и весь путь отфильтровывается. Результат: ничего (пустая последовательность).