Продолжается подписка на наши издания! Вы не забыли подписаться?

Назад

XPath

XML Path Language (XPath) – это язык, разработанный специально для использования с XML и применяемый для поиска узлов и наборов узлов XML-документа в таких технологиях, как DOM и XSLT. Он представляет собой некий гибрид между указанием пути в командной строке и регулярными выражениями. XPath представляет XML-документ в виде дерева из узлов различных типов – элементов, атрибутов и текста. Выражения XPath могут идентифицировать эти узлы по их типу, имени и значениям, а также по взаимоотношениям узлов в документе.

Например, запрос "найти элементы 'author' с атрибутом 'period' и значением 'classical', содержащиеся в элементе 'authors' в документе root" может быть записан как XPath-выражение "/authors/author[@period='classical']".

Эти выражения лежат в основе XSLT, позволяя ассоциировать найденные с помощью таких выражений ветви документа с XSLT–шаблоном. Выражения, использующие синтаксис XPath, могут использоваться напрямую в XML Document Object Model (DOM).

Важной разновидностью XPath-выражений являются location path:, выражения, выбирающие набор узлов, относящихся к узлу контекста. Результатом выполнения такого выражения является набор узлов, содержащий выбранные узлы.

Синтаксис XPath

XML-документы представляют собой иерархию, или дерево узлов, в чем-то схожее с иерархией каталогов и файлов в файловой системе. Этом и объясняется сходство синтаксиса URL и XPath. В следующей таблице приведено сравнение некоторых свойств.

Файловая система (URL)

XPath

Иерархия каталогов и файлов.

Иерархия элементов и других узлов XML-документа.

Файлы на каждом из уровней имеют уникальные имена. URL всегда идентифицирует один файл.

Имена элементов на каждом уровне могут быть неуникальны. Шаблоны XPath соответствуют набору всех соответствующих элементов.

Путь вычисляется относительно конкретного каталога, именуемого текущим каталогом.

Путь вычисляется относительно конкретного узла, в запросе именуемого контекстом.

В следующем фрагменте XML-документа представлена простая иерархия, которая будет использоваться для демонстрации некоторых основных возможностей XPath-запросов.

<authors>
  <author>
    <name>Victor Hugo</name>
    <nationality>French</nationality>
  </author>
  <author period="classical">
    <name>Sophocles</name>
    <nationality>Greek</nationality>
  </author>
  <author>
    <name>Leo Tolstoy</name>
    <nationality>Russian</nationality>
  </author>
  <author>
    <name>Alexander Pushkin</name>
    <nationality>Russian</nationality>
  </author>
  <author period="classical">
    <name>Plato</name>
    <nationality>Greek</nationality>
  </author>
</authors>

При исполнении XPath-запроса всегда имеется так называемый контекст исполнения, то есть текущая ветка, относительно которой производится поиск. Это сходно с активным каталогом при выполнении команды CD файловой системы. Как контекст XPath-запроса может использоваться любой узел XML-документа. В XSLT контекстом для запроса является узел, в данный момент обрабатываемый элементами <xsl:template> или <xsl:for-each>. При использовании XPath непосредственно из DOM вы определяете контекст, выполняя запрос из конкретного узла. Приведенные ниже примеры используют контекст корневого элемента XML-документа.

Основным понятием XPath является путь в XML-иерархии. Если выполнить приведенный ниже запрос, начиная с корневого элемента приведенного выше примера, этот запрос проходит по иерархии вплоть до элемента <name>.

authors/author/name

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

Кроме описания пути, XPath может включать wildcards (групповые символы). Элемент с любым именем обозначается символом "*".

authors/*/name

Предыдущий запрос соответствует всем элементам name, но не требует, чтобы они находились в элементе <author>. Вот еще один пример, находящий элементы <name> и <nationality>.

authors/author/*

Ветвления пути могут быть описаны с помощью квадратных скобок. Следующий запрос ищет ветку элемента <author>, и означает, что рассматриваться должны только элементы <author> с дочерним nationality.

authors/author[nationality]/name

В запросе можно использовать операторы сравнения. Следующий запрос возвращает имена русских авторов. Заметьте, что сравнения можно использовать только в квадратных скобках.

authors/author[nationality='Russian']/name

XML-атрибуты в запросе обозначаются символом "@" перед именем атрибута. Атрибут может проверяться как ветка основного пути, но запрос может и целенаправленно искать узлы атрибутов. Следующий пример возвращает авторов классического периода.

authors/author[@period="classical"]

Таблица 4.Операторы и специальные символы XPath

Оператор

Описание

/

Выбирает дочерние элементы коллекции, находящейся слева от него. При использовании в начале шаблона означает поиск от корневого элемента.

//

Рекурсивный спуск; ищет указанный элемент на любой глубине. При использовании в начале шаблона означает рекурсивный поиск от корневого элемента.

.

Текущий контекст.

*

Wildcard, выбирает все элементы, независимо от имени.

@

Атрибут; префикс имени атрибута. При использовании без имени атрибута выбирает все атрибуты, независимо от их имени.

:

Сепаратор пространств имен. Отделяет префикс пространства имен от имени элемента или атрибута.

( )

Группирует операции для явного задания очередности.

[ ]

1. Накладывает фильтр.

2. Используется для индексации коллекции.

+

Сложение.

-

Вычитание.

div

Деление с плавающей точкой (согласно IEEE 754).

*

Умножение.

mod

Возвращает остаток при делении с остатком.

Коллекции

Возвращаемые XPath-запросами коллекции сохраняют порядок, иерархию и свойства исходного документа, если не указано, что их надо изменить. Поскольку атрибуты неупорядочены по определению, они и возвращаются неупорядоченными.

Коллекция всех элементов данного тега выражается собственно именем тега. То есть, вернее сказать, элементы выбираются из текущего контекста использованием "./", но текущий контекст используется по умолчанию и его не нужно явно указывать.

Так, следующие примеры равнозначны:

./first-name
first-name

Индексация коллекций

Выражения XPath позволяют легко найти определенный узел документа. Нужно просто включить в выражение порядковый номер в квадратных скобках. Этот номер отсчитывается от нуля (номер первого элемента – ноль).

Квадратные скобки [] старше символов / и //. Выражение "//comment()[3]" интерпретируется как "//(comment()[3])", и выбирает все элементы comment с индексом 3 относительно его родителя по всему документу. Это отличается от выражения "(//comment())[3]", выбирающего третий comment из набора всех элементов comment относительно родителя. Первое выражение может вернуть несколько элементов comment, а второе вернет только один.

Например, следующее выражение находит первый элемент author.

author[0]

А это выражение найдет третий элемент author из имеющих дочерний элемент:

author[first-name][2]

Заметьте, что индексы отсчитываются относительно родительского элемента. Посмотрите на следующие данные:

<x>
  <y/>
  <y/>
</x>
<x>
  <y/>
  <y/>
</x>

Найдем первое y для каждого x.

x/y[0]

Найдем первое y из всего набора элементов y внутри элементов x.

(x/y)[0]

Найдем первое y от первого x.

x[0]/y[0]

Поиск последнего элемента коллекции

Функция last возвращает True для последнего элемента коллекции.

Например, следующее выражение найдет последнюю книгу:

book[last()]

Фильтрация

К любой коллекции можно применить ветвления и условия поиска с помощью наложения фильтра, [pattern]. Фильтр аналогичен SQL-выражению WHERE. Фильтр применяется последовательно ко всем элементам коллекции и выдает True/False в зависимости от того, удовлетворяет ли сравниваемый элемент фильтру. Любой элемент, не соответствующий фильтру, не включается в результирующую коллекцию.

Заметьте, что на заданном уровне выражения может быть применено любое количество фильтров. Пустые фильтры недопустимы.

Фильтры всегда применяются относительно контекста. Другими словами, выражение "book[author]" означает, что для каждого найденного элемента book будет выполнена проверка наличия дочернего элемента author. Сходным образом, "book[author = 'Bob']" означает, что для каждого найденного элемента book будет выполнена проверка наличия дочернего элемента author со значением "Bob". Можно также проверять значение контекста с помощью точки (.). Например, "book[. = 'Trenton']" означает, что для каждого элемента book, найденного в текущем контексте, проверяется, что его значение равно "Trenton".

Если фильтр сравнивает значения элементов и должен быть применен более чем к одному дочернему элементу, можно использовать ключевые слова any и all. Если эти слова не используются, для сравнения используется только первый найденный дочерний элемент.

Найти все книги, содержащие хотя бы один элемент [excerpt]:

book[excerpt]

Найти все названия книг, содержащие хотя бы один элемент [excerpt]:

book[excerpt]/title

Найти всех авторов книг, причем книги должны содержать хотя бы один элемент [excerpt], а у автора должна быть хотя бы одна степень:

book[excerpt]/author[degree]

Найти все книги с авторами, имеющими хотя бы одну степень:

book[author/degree]

Операторы

Фильтры могут содержать булевы выражения, выражения сравнения и выражения присвоения. В следующей таблице приведены символы, используемые в реализации XSLT фирмы Микрософт.

Оператор

Описание

and

Логическое "и"

or

Логическое "или"

not()

Отрицание

=

Равенство

!= *

Неравенство

< *

Меньше

<= *

Меньше или равно

> *

Больше

>= *

Больше или равно

|

Объединение, возвращает объединение двух наборов узлов

* Метод расширенного XPath

Синтаксис W3C для операторов использует пробелы и другие разделители, а реализация Микрософт (версия 2.5)– знак доллара ($). В синтаксисе W3C, бинарные ключевые слова формы $xxx$ могут быть выражены как wsxxxws, где ws – ограничитель, которым может быть пробел, одинарные или двойные кавычки. Унарные операторы, например, not(), используют функциональную запись. Хотя реализация Микрософт и поддерживает оба синтаксиса, из соображений совместимости лучше использовать синтаксис W3C.

Порядок приоритетности (от высшего к низшему) для операторов сравнения и булевых операторов приведен в следующей таблице.

1

( )

Группировка

2

[ ]

Фильтрация

3

/ и //

Операции с путем

4

< <= > >=

Сравнения

5

= !=

Сравнения

6

|

Объединение

7

not()

Булево "нет"

8

and

Булево "и"

9

or

Булево "или"

Булевы выражения могут соответствовать всем узлам с конкретным значением или всем узлам со значениями в определенном промежутке.

Все операторы чувствительны к регистру ввода.

Булевы операторы and и or выполняют операции "логическое и" и "логическое или", соответственно. Эти операторы в сочетании с группирующими скобками можно использовать для создания замысловатых логических выражений.

Например, следующее выражение находит все элементы author, содержащие хотя бы по одному элементу degree и award.

author[degree and award]

А это – все элементы author, содержащие хотя бы один из элементов degree или award, и, по крайней мере, один элемент publication.

author[(degree or award) and publication]

Булев оператор not служит для отрицания значения фильтрующего выражения. Например, следующее выражение находит все элементы author, содержащие по крайней мере один элемент degree и не содержащие элементов publication.

author[degree and not(publication)]

Сравнения

Для операций сравнения применяются два операнда: вектор или скаляр (значение с левой стороны, или lvalue), который сравнивается со скалярным значением (значение с правой стороны, или rvalue). rvalue должно быть скалярным или приводимым к скалярному в процессе обработки выражения.

Если lvalue сравнения – это множество, для операторов сравнения применяется семантика "any". Таким образом, результат сравнения равен True, если хотя бы один из членов множества отвечает условию.

lvalue выражения не может быть литералом. Например, "3" = a недопустимо. Если rvalue – это атрибут, text(lvalue) сравнивается с text(rvalue).

Все элементы и атрибуты - это строки, но они автоматически приводятся к целочисленному виду для числовых сравнений. Числовые литералы при операциях сравнения приводятся к типам long или double, как показано в следующей таблице.

Тип литерала

Сравнение

Пример

String

text(lvalue) оператор text(rvalue)

a < GGG

Integer

(long) lvalue оператор (long) rvalue

a < 3

Real

(double) lvalue оператор (double) rvalue

a < 3.1

Для разделения строк в выражениях можно использовать одиночные или двойные кавычки. Это упрощает создание фильтров в скриптовых языках.

Например, следующее выражение находит все элементы author с именем Bob.

author[last-name = "Bob"]

Находит все элементы degree, у которых атрибут from не равен "Harvard".

degree[@from != "Harvard"]

Находит все элементы author, у которых значение "last name" такое же, как у элемента /guest/last-name.

author[last-name = /guest/last-name]

Находит все элементы author, содержащие текст "Matthew Bob".

author[. = "Matthew Bob"]

Логика преобразования данных при сравнениях

При сравнении операндов разных типов данных производятся следующие преобразования.

Бинарные операторы сравнения

Оператор"|"

Оператор "|", он же оператор объединения, возвращает объединенные результаты двух запросов. Можно использовать несколько операторов объединения для объединения результатов нескольких запросов. Оператор объединения сохраняет порядок документа и не возвращает дубликатов.

Реализация Microsoft расширяет область действия этого оператора. Его можно использовать в любом месте запроса, а не только на верхнем уровне. Для поддержки такого поведения введено следующее ограничение: все объединяемые запросы должны относиться к одному поддереву и иметь один корень.

Как DOM определяет контекст для XPath-выражения

Парсер MSXML содержит методы, которые позволяют использовать строки XPath. Метод selectNodes, например, возвращает список узлов, как показано в следующем примере:

items = xmldoc.selectNodes("invoices/invoice/items/item");

Поиск по дереву всех узлов "item" мог бы потребовать приличного количества кода. Метод selectNodes в сочетании с выражением XPath позволяет выбрать эти узлы с помощью единственной строки кода.

В большинстве случаев возвращенная коллекция узлов впоследствии подвергается дальнейшей обработке. Следующий пример показывает, как вычислить общую стоимость для элемента "invoice".

function invoiceTotal(invoice)
{
  invoice = invoice.nextNode()
  items = invoice.selectNodes("items/item");
  var sum = 0;
  for (var item = items.nextNode(); item; item = items.nextNode())
  {
    var price = item.selectSingleNode("price").nodeTypedValue;
    var qty = item.selectSingleNode("qty").nodeTypedValue;
    if (qty >= 10)
      price = 0.9*price;
    sum += price * qty;
  }
  return sum;
}

Этот пример содержит два контекста запроса. Первый запрос выполняется в контексте элемента "invoice", и строка поиска отражает это, начинаясь с элемента "items". Цикл for проходит по найденным узлам и использует каждый из них как контекст дальнейшего поиска. Следующие запросы работают в элементах "item", и ищут в них дочерние элементы "price" и "qty". Это показывает, что узел, относительно которого выполняется selectNodes, определяет контекст поиска.

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

items = xmldoc.selectNodes("invoices/invoice");
items = xmldoc.selectNodes("*/invoice");
items = xmldoc.documentElement.selectNodes("invoice");

Метод selectSingleNode возвращает первый соответствующий запросу узел, что позволяет легко получить конкретный узел, не извлекая его из NodeList. Это эквивалентно "selectNodes(pattern).item(0)". Использование этого метода позволяет упростить код предыдущего примера.

{
  invoice = invoice.nextNode()
  items = invoice.selectNodes("items/item");
  var sum = 0;
  for (var item = items.nextNode(); item; item = items.nextNode())
  {
    var price = item.selectSingleNode("price").nodeTypedValue;
    var qty = item.selectSingleNode("qty").nodeTypedValue;
    sum += price * qty;
  }
  return sum;
}

В этом примере selectNodes по прежнему используется для выборки набора элементов "item". Если ожидается одиночный узел, например, элементы "price" и "qty", можно использовать selectSingleNode.

Далее


Copyright © 1994-2016 ООО "К-Пресс"