Практическая работа с XML: Введение в XPath 2.0

Содержание

XPath 2.0 лежит в основе двух стандартов первостепенной важности, которые на данный момент находятся в последней стадии разработки в W3C: XSLT 2.0 и XQuery. Сам XPath был существенно переработан с целью повышения гибкости и эффективности языка. В этой статье Бенуа Маршаль (Beno?t Marchal) расскажет о том, как новая модель данных XPath позволяет разработчикам легко создавать сложные запросы.

Серьезная переработка знакомого стандарта

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

Рекомендация XPath 2.0 лежит в основе XSLT 2.0 и XQuery 1.0. Оба этих языка используют XPath в качестве ядра для выполнения запросов. В остальном они лишь добавляют операторы для форматирования результатов выполнения.

Существует множество различий между XPath 1.0 и XPath 2.0, в том числе следующих: * Новая модель данных, в основе которой лежит понятие последовательности (sequence), а не набора узлов (node set). * Возможность присваивать значения переменным. Ранее это делалось исключительно средствами внешнего языка, например, XSLT. * Полная поддержка типов данных XML Schema. * Множество новых функций, в том числе для работы с регулярными выражениями, датой и временем, а также для манипулирования строками. * Поддержка комментариев. Разумеется, это не критически важная функция, однако они полезны при отладке запросов, так как позволяют комментировать фрагменты кода в процессе тестирования.

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

Последовательности в XPath 2.0

Все элементы данных в процессе обработки в XPath 2.0 представляются в виде последовательностей. Последовательность – это упорядоченный, разнородный набор объектов. Объектами могут быть узлы XML-документа или атомарные значения. Значения могут быть любого, в том числе сложного, типа, который определен в рекомендации XML Schema. Для объявления последовательности в XPath необходимо заключить набор объектов в круглые скобки, разделив их запятыми:

(2, 'declencheur', 5.10)

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

/po:PurchaseOrder/po:ProductList/po:Name.

Однако в отличие от своего предшественника, в XPath 2.0 переходы обозначают элементы последовательности, а не просто узлы в дереве. При этом узлы могут выступать в качестве элементов.

Все ключевые понятия в XPath 2.0 были изменены с учетом появления последовательностей. Например, функции, которые в XPath 1.0 принимали наборы узлов в качестве аргументов, теперь работают с последовательностями. Древовидная модель данных, характерная для XPath 1.0, имела определенные преимущества, учитывая, что XML-документы обладают иерархической структурой. Однако она также имела существенные ограничения, в частности, нельзя было генерировать деревья, а следовательно, невозможно передавать результат от одного запроса к другому для дальнейшей обработки. Было также невозможно писать сложные запросы, например в стиле SQL.

Работа с последовательностями

Как упоминалось ранее, в XPath 2.0 поддерживается синтаксис XPath 1.0, но при этом добавляется несколько новых операторов для работы с последовательностями. Первым из них я рассмотрю выражение for, которое (как следует из названия) служит для организации цикла по элементам последовательности.

Типичный пример выражения for показан в листинге 1.

Листинг 1. Пример использования for в XPath 2.0

for $line in /po:PurchaseOrder/po:OrderLines/po:Line
  return $line/po:Price * $line/po:Quantity

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

(29.99, 89.98, 80, 3.1)

Листинг 2. XML-документ для описания заказа

<?xml version="1.0" encoding="ISO-8859-1"?>
<po:PurchaseOrder xmlns:po="http://www.marchal.com/2006/po">
    <po:Buyer>Pineapplesoft</po:Buyer>
    <po:Seller>Bookstore</po:Seller>
    <po:OrderLines>
        <po:Line>
            <po:Code type="ISBN">0-7897-2504-5</po:Code>
            <po:Quantity>1</po:Quantity>
            <po:Description>XML by Example</po:Description>
            <po:Price>29.99</po:Price>
        </po:Line>
        <po:Line>
            <po:Code type="ISBN">0-672-32054-1</po:Code>
            <po:Quantity>2</po:Quantity>
            <po:Description>Applied XML Solutions</po:Description>
            <po:Price>44.99</po:Price>
        </po:Line>
        <po:Line>
            <po:Code type="ISBN">2-10-005763-4</po:Code>
            <po:Quantity>2</po:Quantity>
            <po:Description>Huit Solutions Concretes avec XML et Java</po:Description>
            <po:Price>40.00</po:Price>
        </po:Line>
        <po:Line>
            <po:Quantity>1</po:Quantity>
            <po:Description>Internet Magazine</po:Description>
            <po:Price>3.10</po:Price>
        </po:Line>
    </po:OrderLines>
</po:PurchaseOrder>

В листинге 1 for является ключевым словом, обозначающим, что необходимо перебрать всю последовательность строк и присвоить каждую из них переменной $product. Как и в XPath 1.0 для выбора последовательности используется путь: po:PurchaseOrder/po:OrderLines/po:Line.

Далее следует ключевое слово return, которое динамически создает последовательность. Затем на каждой итерации цикла к ней добавляется ноль, один или более элементов, после чего последовательность возвращается в качестве результата.

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

Листинг 3. Обработка результатов запроса в XPath 2.0

fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line
  return $line/po:Price * $line/po:Quantity)

Как этот пример выглядел бы раньше

В листинге 4 показан аналог того же алгоритма, что и в листинге 3, но реализованный средствами XPath 1.0 и XSLT 1.0.

Листинг 4. Вычисление общей суммы заказа в XPath 1.0

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:po="http://www.marchal.com/2006/po"
                xmlns:exslt="http://exslt.org/common"
                version="1.0">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:variable name="lines">
            <xsl:for-each select="/po:PurchaseOrder/po:OrderLines/po:Line">
                <line-total>
                    <xsl:value-of select="po:Price * po:Quantity" />
                </line-total>
            </xsl:for-each>
        </xsl:variable>
        <xsl:value-of select="sum(exslt:node-set($lines)/line-total)" />
    </xsl:template>
</xsl:stylesheet>

В листинге 4 также вычисляются суммы для каждой строки заказа, которые затем передаются в функцию sum(). Однако в XPath 1.0 переменные должны объявляться во внешнем языке (в данном случае XSLT), в частности, в этом примере промежуточные результаты сохраняются в переменной lines, которая затем передается во второе выражение XPath, вычисляющее общую сумму заказа.

Преимущества XPath 2.0 в том, что касается выразительности языка, становятся очевидны при сравнении листингов 3 и 4. В последнем случае требуется два выражения XPath вместо одного, при этом для передачи промежуточных результатов требуется дополнительный язык – XSLT. Кроме того, листинг 4 сложнее для понимания и труднее поддается оптимизации из-за разделения обработки запроса на два выражения XPath.

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

В XPath 2.0 также появились выражения для условного перехода (if), приведенные в листинге 5. Синтаксис говорит сам за себя: в зависимости от истинности выражения в скобках, вычисляется либо секция then, либо секция else.

Листинг 5. Пример условного выражения

if(/po:PurchaseOrder/po:Seller = 'Bookstore') then 'ok' else 'ko'

Кванторы

Обсуждение последовательностей было бы неполным, если не упомянуть о кванторах. Вкратце, кванторы (или квантифицированные выражения – quantified expressions) представляют собой проверки, выполняющиеся над последовательностью в целом. Существуют два квантора, обозначающиеся ключевыми словами every и some.

В листинге 6 показан пример квантора every. Он состоит из двух частей: сначала значение последовательности присваивается переменной (аналогично циклу), а затем указывается условие, которому должны удовлетворять элементы последовательности. Различие между квантором и циклом заключается в том, что результатом вычисления квантора является булево значение, а цикла – последовательность.

Если быть более точным, то значением квантора every является true, если все элементы последовательности удовлетворяют условию. Квантор some является истинным, если условие выполняется хотя бы для одного элемента.

Листинг 6. Пример использования кванторов

every $line in /po:PurchaseOrder/po:OrderLines/po:Line satisfies $line/po:Code

Результатом вычисления выражения в листинге 6 над данными, показанными в листинге 2, будет false, потому что четвертая строка не содержит элемент po:Code. Если же заменить квантор every на some, то результатом будет true, ввиду того, что элемент po:Code содержится как минимум в одной строке.

Бесконечное множество комбинаций

Мощь XPath 2.0 обуславливается возможностью комбинировать выражения, создавая тем самым сложные запросы. Например, в листинге 7 показано выражения для подсчета общей суммы заказа в соответствии с другой формулой: в подсчете участвуют только строки, включающие код продукта, другие же попросту игнорируются (допустим, что данные продукты не могут быть доставлены заказчику). Код выглядит очень просто, так как все, что пришлось добавить в выражение цикла – это условное выражение if, возвращающее пустую (empty) последовательность в случае отсутствия кода продукта.

Листинг 7. Комбинирование выражений

fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line
  return if($line/po:Code) then $line/po:Price * $line/po:Quantity else ())

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



9 февраля 2009 г.
Бенуа Маршаль
http://www.ibm.com/developerworks/ru/library/x-wxxm35/index.html