Что же представляет из себя xpath-запрос?
XPath (XML Path Language) — язык запросов к элементам XML или XHTML документа. XML имеет древовидную структуру. В документе всегда имеется корневой элемент. У элемента дерева всегда существуют предки (исключение — корневой элемент, у которого предков нет) и могут существовать потомки. Каждый элемент дерева находится на определенном уровне вложенности. У элементов на одном уровне бывают предыдущие и следующие за ним элементы. Строка XPath — это фактически путь к элементу в дереве, где каждый уровень разделяется косой чертой «/». В результате обработки выражения XPath получается объект, который может быть:
набор узлов (node-set) — неупорядоченный набор узлов без дубликатов
булево значение (boolean) — true или false
число (number) — число с плавающей точкой
строка (string) — последовательность UCS символов
Небольшой примерчик для старта:
Результатом выполнения следующего XPath запроса будет узел <span>:
/html/body/*/span
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
Следующий запрос вернет несколько элементов (div#first и div#second):
/html/body/div
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div> <div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
* — обозначает любое имя или набор символов
/ — определяет уровень дерева
Если в запросе первым символом стоит «/» , то путь адресации считается абсолютным (то есть от корня документа). Корень документа всегда является контекстом по умолчанию. Контекст — это текущий полученный узел или набор узлов, относительно которых рассчитывается следующий шаг.
Оси
Оси это основа запросов XPath и их обязательная часть.
ancestor:: — возвращает множество предков.
ancestor-or-self:: — возвращает множество предков и текущий элемент.
attribute:: — возвращает множество атрибутов текущего элемента.
child:: — возвращает множество потомков на один уровень ниже.
descendant:: — возвращает полное множество потомков.
descendant-or-self:: — возвращает полное множество потомков и текущий элемент.
following:: — возвращает необработанное множество, ниже текущего элемента.
following-sibling:: — возвращает множество элементов на том же уровне, следующих за текущим.
namespace:: — возвращает множество имеющее пространство имён (то есть присутствует атрибут xmlns).
parent:: — возвращает предка на один уровень назад.
preceding:: — возвращает множество обработанных элементов исключая множество предков.
preceding-sibling:: — возвращает множество элементов на том же уровне, предшествующих текущему.
self:: — возвращает текущий элемент.
Для наиболее часто используемых осей существуют сокращения:
attribute:: — можно заменить на «@»
child:: — часто просто опускают
descendant:: — можно заменить на «.//»
parent:: — можно заменить на «..»
self:: — можно заменить на «.»
Для приведенного выше примера
/html/body/*/span
полный синтаксис будет иметь вид
/child::html/child::body/child::*/child::span
Чаще xpath-запрос начинают с «.//» или «//», это делает путь к элементу относительным. Символы ".//" в начале запроса возвращают полное множество потомков, которые являются дочерними для корня документа, т.е. все элементы на текущей странице. В данном случае точку в начале запроса можно опустить, потому что корневой элемент уже является контекстом.
Первый пример можно переписать так:
.//div/span
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
В то время как следующий xpath-запрос вернет оба элемента span:
.//span
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
Важную роль в построение запросов играют предикаты — это необязательная часть, заключаемая в квадратные скобки, в которой могут содержаться оси, условия проверки, функции и операторы. В ходе обработки предиката из полученного на шаге набора узлов исключаются узлы, не прошедшие условие проверки. Например, найдем элемент span, находящийся внутри любого элемента с id = «first»
.//*[@id="first"]/span
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
Примеры использования осей при построении запросов:
1. following-sibling::
.//*[@id="first"]/following-sibling::span
или
.//*[@id="second"]/following-sibling::*[1]
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>
2. parent::
.//span[1]/..
<html>
<body>
<div id="first">
<span>span внутри div</span>
</div>
<div id="second"></div>
<span>некоторый текст</span>
</body>
</html>