XPath

Текущая версия страницы пока не проверялась опытными участниками и может значительно отличаться от , проверенной 8 сентября 2017 года; проверки требуют .

На данный момент самой популярной версией является XPath 1.0. Это связано с отсутствием поддержки XPath 2.0 со стороны открытых библиотек. В частности, речь идёт о libxml2, от которой зависит поддержка языка в браузерах, с одной стороны, и поддержка со стороны серверного интерпретатора, с другой.

XML имеет древовидную структуру. В самостоятельном XML-документе всегда имеется один корневой элемент (инструкция <?xml version="1.0"?> к дереву элементов отношения не имеет), в котором допустим ряд вложенных элементов, некоторые из которых тоже могут содержать вложенные элементы. Так же могут встречаться текстовые узлы, комментарии и инструкции. Можно считать, что XML-элемент содержит массив вложенных в него элементов и массив атрибутов.

У элементов дерева бывают элементы-предки и элементы-потомки (у корневого элемента предков нет, а у тупиковых элементов (листьев дерева) нет потомков). Каждый элемент дерева находится на определённом уровне вложенности (далее — «уровень»). Элементы упорядочены в порядке расположения в тексте XML, и поэтому можно говорить об их предыдущих и следующих элементах. Это очень похоже на организацию каталогов в файловой системе.

Строка XPath описывает способ выбора нужных элементов из массива элементов, которые могут содержать вложенные элементы. Начинается отбор с переданного множества элементов, на каждом шаге пути отбираются элементы, соответствующие выражению шага, и в результате оказывается отобрано подмножество элементов, соответствующих данному пути.

Элементы пути преимущественно пишутся в XPath в краткой форме. Полная форма приведённого выше пути имеет вид /child::html/child::body/child::*/child::span[attribute::class]

Анализ пути ведётся слева направо, и начинается либо в контексте первого элемента корневого узла (в данном примере это элемент html), и тогда по оси child:: будут вложенные в него элементы (в данном примере это один элемент body), что удобно в случае обработки обычного XML-документа с одним корневым узлом, либо, если в начале XPath указан символ /, в контексте со всеми корневыми элементами переданого XML по оси child:: (в даном примере это будет один элемент html). На каждом шаге адресации в текущем контексте отбираются элементы, подходящие под указанные в шаге условия, и их перечень берётся как контекст для следующего шага или как возвращаемый результат.

Таким образом, первый шаг /child::html явным образом делает текущим контекстом для следующего шага перечень из одного элемента html, что было бы и так сделано неявно, если этот шаг не был обозначен.

На втором шаге адресации в данном примере (шаг child::body) контекстом является перечень из одного элемента html. Ось child:: говорит о том, что необходимо смотреть на имена вложенных элементов в текущем контексте, а условие проверки body говорит о том, что в формируемый набор элементов нужно включить те узлы, у которых имя body. Таким образом, в ходе второго шага адресации получаем набор узлов, состоящий всего из одного элемента body, который и становится контекстом для третьего шага.

Третий шаг адресации: child::* . Ось child:: содержит всех непосредственных потомков элемента body, а условие проверки * говорит о том, что в формируемый перечень нужно включить элементы основного типа с любым именем. В ходе этого шага получаем перечень, состоящий из трёх элементов div, одного span и одного элемента img — итого, пять элементов.

Четвёртый шаг адресации: child::span/@class. Его контекстом является перечень из пяти элементов, поэтому исходящий перечень создаётся в пять проходов (за пять итераций). При первой итерации узлом контекста становится первый div. Согласно заданной оси child:: и правилу проверки span, в набор должны включаться непосредственные потомки этого div-а, имя которых равно span. Там такой один. При второй итерации в набор ничего добавляться не будет, так как у второго div нет потомков. Третья итерация увидит сразу три элемента span. Четвёртая ничего не увидит, так как у элемента span нет потомков span, а то что он сам span — не важно, ведь просматриваются именно потомки. Пятая тоже ничего не увидит, у элемента img тоже нет потомков span. Итак, в ходе проверки мог бы быть получен набор узлов, состоящий из четырёх элементов span. Это и было бы контекстом для последующей обработки, не будь на этом шаге указано предиката.

Но так как предикат на четвёртом шаге есть, по мере выполнения каждого из пяти проходов будет производиться дополнительная фильтрация отбираемых элементов. В данном случае у предиката ось attribute:: говорит о необходимости проверить, есть ли у отбираемого узла атрибуты, а условие class требует оставить лишь те узлы, у которых задан атрибут с именем class. И поэтому на первой итерации единственный найденный span фильтрацию предикатом не пройдёт, на третьей итерации фильтрацию пройдут два элемента из трёх, и в итоге, несмотря на то, что фильтрация происходит за пять итераций, в окончательный набор попадают только два элемента span.

Оси — это база языка XPath. Для некоторых осей существуют сокращённые обозначения.

В рамках содержимого оси отбор выполняется согласно выражению, определяющему отбираемые элементы.

Предикаты это логические выражения в квадратных скобках, составленные по тем же принципам, что и выражение отбора. Выражения, возвращающие не логическое значение, а пустой набор элементов, считаются ложными. Выражение, возвращающее число, считается выражением, сравнивающим число с position(). Когда предикатов более одного, каждый из них фильтрует результаты фильтрации предыдущим предикатом.