Что нового в XPath 2.0

Источник: What's New in XPath 2.0
by Evan Lenz, XML.com,
Copyright ¤ 2002 O'Reilly & Associates, Inc.
Перевод на русский язык - Copyright ¤ 2002 Алексей Валиков.

Эта статья предлагает краткую экскурсию по некоторым новым особенностям языка XPath 2.0. В ней полагается, что вы уже имеете базовое понимание языка XPath 1.0, и что, скорее всего, вы использовали его в контексте языка XSLT. Эта статья ни в коем случае не является полным обзором, она только лишь указывает на некоторые наиболее примечательные особенности.

Связь между языками XPath 1.0 и XPath 2.0

И рекомендация языка XPath 1.0 и последний рабочий черновик спецификации языка XPath 2.0 говорят, что "XPath является языком для обращения к частям XML-документа". Это довольно подходящая характеристика XPath 1.0. (Конечно, тут не упоминается о том, что можно использовать арифметические вычисления и строковые, численные и логические выражения, просто эти возможности сведены к минимуму.) С другой стороны, как характеристика XPath 2.0, это определение оставляет желать лучшего. XPath 2.0 это гораздо более мощный язык, который оперирует на гораздо большем множестве типов. Лучше описать XPath 2.0 как язык выражений для обработки последовательностей со встроенной поддержкой XML-документов. А выполнение запросов? Но разве это не задача языка XQuery?

Связь между языками XPath 2.0 и XQuery 1.0

Вот уже свыше года рабочие группы Консорциума W3 по языкам XSL и XML Query вплотную работали вместе. Целью было соединить XSLT 2.0 и XQuery 1.0 настолько, насколько это технически и политически осуществимо, и дать этому общему подмножеству двух языков имя "XPath 2.0". На практике это означает, что движущие силы, стоящие за XPath 2.0 включают не только документ "Требования к XPath 2.0" но и многие из "Требований к языку XML Query".

Язык XPath 2.0 является строгим синтаксическим подмножеством языка XQuery 1.0. На самом деле, черновые спецификации и грамматики обоих языков были автоматически сгенерированы из единого источника (используя, естественно, XML и XSLT). Хотя черновая спецификация языка XPath, строго говоря, не является частью спецификации XQuery (в силу того, что несколько параграфов были посвящены исключительно этому языку), это близко к правде. Как бы то ни было, 80% текста чернового варианта спецификации XQuery является общим для обоих документов. Если говорить о подмножествах языков, то общая часть XPath 2.0 и XQuery 1.0 довольно велика. Оптимистическое заключение состоит в том, что как только все трудности изучения XPath 2.0 останутся позади, вы приятно обнаружите, что вы почти закончили изучать XQuery.

В сущности, специфические особенности XQuery в основном состоят из высокоуровневых механизмов инкапсуляции запросов, таких как определения функций, объявления пространств имен и импортирование схем, равно как и конструкторов элементов. Язык XSLT 2.0, еще одна важная область применения XPath 2.0, не нуждается в этих механизмах, поскольку, там, по большей части, предлагаются их собственные версии. Таким образом, эти механизмы не включаются в общее подмножество этих языков.

Поддержка языка схем XML-документов XML Schema

Как вы помните, в XPath 1.0 поддерживались только четыре типа данных выражений:

Достоинством этого была простота, а недостатком - ограниченность в случае, когда дело доходит до обработки типизированых данных, таких, как даты. С другой стороны, в XPath 2.0, вводится поддержка примитивных типов данных языка XML Schema, что немедленно предоставляет пользователю доступ к 19 простым типам, включая даты, года, месяцы, универсальные идентификаторы ресурсов, и т.д. В дополнение к этому предусмотрен ряд функций и операторов для обработки и создания данных этих различных типов. Их спецификация содержится в документе "Функции и Операторы XQuery 1.0 и XPath 2.0 ".

Для исчерпывающей информации о том, какие типы данных могут возвращать выражения в XPath 2.0, обратитесь к документу "Модель данных XQuery 1.0 и XPath 2.0 Data Model". Для наших целей достаточно сказать, что выражения могут возвращать данные простых типов, узлы и последовательности узлов или простых типов. Как мы увидим, на самом деле всякое выражение возвращает последовательность.

Узлы в XPath 2.0 имеют, то же базовое определение, что и в XPath 1.0, за исключением того, что некоторые их типы (элементы и атриубты) теперь могут ассоциироваться с типами данных XML Schema и обрабатываться по существу. Как и в XPath 1.0, существуют семь типов узлов: узлы документов, элементы, атрибуты, узлы пространств имен, инструкции по обработке, комментарии и текстовые узлы. Здесь единственным отличием в терминологии является то, что "корневые узлы" теперь называются более подходяще, "узлами документов ".

Последовательности, последовательности, последовательности

Поскольку XPath 2.0 обрабатывает последовательности, полезно обсудить, что есть последовательность с точки зрения этого языка и как она себя ведет. Перечисленное ниже есть набор правил, которые следует запомнить. Эти важнейшие истины о последовательностях являются фундаментальными принципами того, как работает XPath 2.0. Их понимание является предпосылкой для более глубокого понимания тонкостей и методов использования XPath 2.0.

Основное правило #1: Все является последовательностью.

Если вы вдруг захотите поразить своих друзей, укажите на любое заданное выражение языка XPath 2.0 (или выражение языка XQuery) и ненароком заметьте, что, очевидно, это выражение возвращает последовательность. Только не нужно портить шутку, открывая им тот секрет, что, на самом деле, все выражения возвращают последовательности.

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

Основное правило #2: Последовательности неглубоки.

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

Например, выражение

(2, 4, (1,2,3), 6)

возвращает точно ту же последовательность, что и выражение

(2, 4, 1, 2, 3, 6)

или выражение

( (((2))), (4,1,2,3,(6)) )

Основное правило #3: Последовательности упорядочены.

В отличие от множеств узлов в XPath 1.0, последовательности упорядочены. Рассмотрим следующее выражение:

(/foo/bar, /foo)

Как вы, возможно, заметили, запятая (,) это оператор для создания (соединения) последовательностей. Поставив /foo после /foo/bar, я создал последовательность, в которой элементы bar предшествуют элементам foo, вне зависимости от их порядка следования в начальном документе. Далее мы увидим, как последовательности в XPath 2.0 могут заменить множества узлов XPath 1.0 без потери функциональности и совместимости.

Основное правило #4: Последовательности могут содержать повторения.

Также, в отличие от множеств узлов XPath 1.0 (и в отличие от множеств вообще), последовательности могут содержать повторные вхождения. Например, мы можем слегка изменить наше предыдущее выражение:

(/foo/bar, /foo, /foo/bar)

Эта последовательность состоит из элементов bar, за которыми следуют элементы foo, за которыми снова следуют те же самые элементы bar. В XPath 1.0 такой набор создать было невозможно, поскольку, по определению, множества узлов не могут содержать повторные вхождения одного и того же узла.

Восход и закат множеств узлов

Если в XPath 1.0 вы хотели обрабатывать набор узлов, то дело всегда приходилось иметь с множествами узлов. В XPath 2.0 понятие множеств узлов было обобщено и расширено. Как мы видели, последовательности могут содержать значения простых типов равно как и узлы. Мы также видели, что последовательности отличаются от множеств узлов тем, что они упорядочены и могут содержать повторения. Естественным образом возникает вопрос: как можно отделаться от множеств узлов без того, чтобы разрушить XPath?

Как имитировать множества в среде, допускающей только последовательности

Верно, множества в XPath 1.0 неупорядочены. Тем не менее, в наиболее общепринятой предметной области XPath, языке XSLT, узлы, содержащиеся во множестве узлов, всегда обрабатываются в некотором порядке. По умолчанию порядком обработки множества узлов является порядок их следования в документе (ибо порядок появления узлов в документе определен для всех узлов в любом случае). В XSLT 2.0 порядком обработки коллекции узлов (то есть последовательности) по умолчанию вовсе не обязательно является порядок их появления в документе, точнее сказать, это собственный порядок последовательности. Чтобы поддерживать обратную совместимость с XPath 1.0, выражения путей (и другие выражения первой версии, такие, как, выражения объединения) определены так, что порядок узлов результата всегда является порядком следования этих узлов в документе. В частности, всякий раз, когда в выражении используется "/", можно ожидать, что порядок узлов результата будет порядком их следования в документе. В дополнение к этому, повторные вхождения узлов автоматически удаляются из результата. Таким образом, XPath 2.0 может имитировать множества в окружении, которое допускает использование только лишь последовательностей.

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

Ключевые слова, которые следует запомнить

В дополнение ко вновь введенным типам данным и функциям, в XPath 2.0 представлены новые операторы, основанные на использовании ключевых слов. Некоторые из них мы рассмотрим ниже.

Операторы, работающие с последовательностями

Пожалуй, самый мощный из новых операторов XPath 2.0 для обработки последовательностей это for- выражение. Оно позволяет выполнять на перебор последовательностей, возвращая для каждого из членов последовательности-аргумента новое значение. Действие этого оператора похоже на возможности инструкции xsl:for-each, однако за тем отличием, что это данное выражение возвращает последовательность, которая в свою очередь также может быть обработана.

Рассмотрим следующий пример, который возвращает последовательность значений простых типов, каждое из равно общей стоимости каждого из предметов в заказе на поставку:

for $x in /order/item return $x/price * $x/quantity

Теперь мы можем подсчитать общую стоимость заказа, используя функцию sum():

sum(for $x in /order/item return $x/price * $x/quantity)

Задачи такого рода решаются при помощи последовательностей в XPath 2.0 гораздо легче, чем они решались в XSLT/XPath 1.0. Без последовательностей эта задача решается гораздо сложнее. Решение обычно включает создание временного "результирующего фрагмента дерева" и, затем, использование функции расширения node-set().

Условные выражения

В числе наиболее мощных (и часто запрашиваемых) конструкций, добавленных в XPath 2.0 находятся условные выражения. Ниже дан пример, включенный в рабочий чернових спецификации XPath 2.0.

if ($widget1/unit-cost < $widget2/unit-cost) 
then $widget1
else $widget2

Кванторы

В XPath 1.0, оператор равенства (=) был одним из наиболее мощных элементов языка. Мощность оператора равенства заключалась в его возможности сравнивать множества узлов. Рассмотрим следующее выражение:

/students/student/name = "Fred"

В XPath 1.0 это выражение возвращает истину в случае, когда имя хотя бы одного из студентов равно "Fred". Это выражение можно назвать экзистенциальным подсчетом, поскольку оно определяет существование элемента, удовлетворяющего определенному условию. Эта функциональность оставлена в XPath 2.0; кроме того, предлагается также и более явный способ проверки:

some $x in /students/student/name satisfies $x = "Fred"

Это более мощная формулировка, поскольку можно заменить условие $x = "Fred" любым желаемым сравнением, не только проверкой равенства. К тому же, в XPath 1.0 нет способа проверить, равно ли имя каждого из студентов строке "Fred". Такая возможность универсального подсчета представлена в XPath 2.0 синтаксисом, близком к синтаксису выражения в предыдущем примере.

every $x in /students/student/name satisfies $x = "Fred"

Пересечения, разности, объединения

Единственным настоящим оператором над множествами в XPath 1.0 был оператор объединения (|). Это значит, что определять принадлежность узла множеству было очень неудобно. Например, для того, чтобы определить, включен ли узел $x во множество /foo/bar, мы должны были написать что-то вроде:

/foo/bar[generate-id(.)=generate-id($x)]

или:

count(/foo/bar)=count(/foo/bar | $x)

Введение в XPath 2.0 оператора intersect облегчает часть этих страданий. Вместо того, чтобы придумывать обходные пути решения этой проблемы, мы можем просто написать

$x intersect /foo/bar

В XPath 2.0 также введен оператор except, который может быть очень удобным, когда нам нужно выбрать все элементы данного множества за исключением определенных узлов. В XPath 1.0, если бы мы, к примеру, хотели выбрать все атрибуты за исключением одного атрибута заданного пространства имен с определенным именем, нам следовало бы написать:

@*[not(namespace-uri()='http://example.com' and local-name()='foo')]

или:

@*[not(generate-id(.)=generate-id(../@exc:foo)]

И снова XPath 2.0 приходит на помощь, предлагая следующую красивую альтернативу:

@* except @exc:foo

Забота о типах данных

Если вы заглянете в спецификацию языка XPath 2.0, вы увидите, что я опустил из рассмотрения множество ключевых слов, включая вещи типа cast, treat, assert, и instance of. Это важные части языка, однако их важность частично зависит от того, в каком окружении вы используете XPath 2.0. Если вы будете использовать XPath в контексте языка XSLT 2.0, возможно, эти операторы не будут нужны вам все время. В некоторых случаях (например, приводя строковое значение к дате) вы непременно захотите их использовать, однако требовать от вас этого никто не будет. В контексте языка XQuery 1.0, тем не менее, вам может быть понадобится довольно близкое знакомство с этими операторами.

Причиной тому то, что XQuery 1.0 разработан как статически типизированный язык. Анализ запросов и оптимизация выполняются на основе знания того, какие типы данных запрос будет возвращать еще до фактического выполнения запросов. Это может быть возможно только, если пользователь в явном виде задает, какой тип возвращает каждое его выражений. Другое достоинство этого подхода состоит в том, что ошибки могут быть найдены заранее, таким образом, усиливая корректность запросов.

Определенно, существует некий компромисс между удобством использования и типовой безопасностью. Для того чтобы отвечать нуждам обоих сообществ (иногда здесь проводится искусственное разделение на решения, ориентированные на документы и на данны), в XPath 2.0 предлагаются средства, при помощи которых окружение может само решать, в пользу какого из подходов оно будет решать этот компромисс. Фактически, язык XPath 2.0 может быть параметризован окружением, в котором он используется. Возможно, это звучит как средство снижения способности к взаимодействию. Несмотря на это, важно определить базовый принцип, стоящий за этим подходом. Этот принцип состоит в том, что любое выражение языка XPath 2.0, не вызывающее ошибки, будет всегда возвращать одинаковый результат в любом языковом окружении. Так, если выражение в одном окружении вызывает ошибку, а в другом - нет, оно никогда не вернет два различных результата. Иными словами, всегда получается либо ошибка, либо правильный ответ. Ни при каких условиях не может быть более чем одного верного ответа.

Вывод, касающийся пользователей языка XSLT, состоит в том, что им не нужно волноваться за большую часть их разработок. Некоторые выражения языка XPath 2.0 могут вызвать "исключения" в окружении языка XQuery, однако те же выражения спокойно отрабатываются в контексте языка XSLT.

Заключение

Похоже, стало ясно, что язык XPath 2.0 представляет собой довольно значительное усовершенствование языка XPath 1.0. Его развитие определялось как потребностями сообщества пользователей XPath 1.0, так и требованиями, предъявленными к XQuery 1.0. Даже если вы не совсем одобряете результат этого развития, сложно не признать, что получилась замечательная разработка. Как бы то ни было, XPath 2.0 станет мощным, стандартным инструментом для нескольких пользовательских сообществ одновременно.