xmlhack.ru logo
>> статьи на xmlhack.ru

Введение в потоковые XML-преобразования

Автор: Оливер Беккер
Автор: Пол Браун
Автор: Пётр Кимприч
Перевод: А.Скробов
Опубликовано на XML.com (26.02.2003, англ.): http://www.xml.com/pub/a/2003/02/26/stx.html
Опубликовано на xmlhack.ru (06.07.2006, рус.): http://xmlhack.ru/texts/06/stx/stx.html
В закладки:   Del.icio.us   reddit

Обзор

В этой статье мы познакомим вас с потоковыми XML-преобразованиями (STX): это язык для создания шаблонов, управляющих XML-преобразованиями, который работает с потоками из событий SAX. STX похож на XSLT 1.0 — язык для создания иерархических шаблонов, управляющих XML-преобразованиями; но в некоторых приложениях, благодаря ряду своих уникальных возможностей, STX оказывается более удобным.

Популярность XSLT росла на протяжении трёх прошедших с его создания лет; отчасти причиной, а отчасти следствием этого было всё более широкое применение XML. В отличие от объектной модели документа (DOM), предоставляющей программисту набор API-функций для работы с XML, XSLT — это нестрого типизированный декларативный язык, предназначенный специально для иерархических преобразований XML-документов. Несмотря на содержавшееся в стандарте XSLT 1.0 предписание:

…XSLT не предполагает использование в качестве полностью универсального языка для XML-преобразований: в основном он разрабатывался для тех видов преобразований, которые необходимы при использовании XSLT в составе XSL.

— XSLT получил широкое распространение именно в качестве универсального инструмента для обработки XML-документов.

SAX — это событийно-ориентированный аналог DOM API, а STX — потоковый аналог XSLT, в котором сохранены многие уже привычные элементы (например, шаблоны и язык выражений, похожий на XPath 1.0), но в качестве нижележащего интерфейса к содержимому XML-документа используется SAX. Про STX нужно сделать ту же оговорку, что уже сделана про XSLT: STX — это не универсальный язык XML-преобразований; кроме того, это не замена для XSLT, и не его новая улучшенная версия.

STX, как и SAX, — это полностью открытый для широкой публики проект, начатый Петром Кимпричем. Рассылка, посвящённая этому проекту, и текущая версия спецификации STX, содержащая полный список его участников, размещены на SourceForge. В настоящее время существуют две реализации STX:

Поясняющий пример

В этом разделе некоторые из сильных сторон STX будут проиллюстрированы на конкретном примере. Представим себе питомник, в котором разводятся различные виды деревьев и цветов; заказы питомнику поступают в простом XML-формате. Ниже приведён сокращённый пример документа с заказами; его полный текст доступен непосредственно в виде XML. Каждый заказ (order) состоит из названия (name), необязательного количества (quantity) со значением по умолчанию 1, и ценой (price) одного экземпляра.



<?xml version="1.0"?>
<order-list>
  <order>
    <name>Тысячелистник обыкновенный</name>
    <quantity>50</quantity>
    <price>2.93</price>
  </order>
  ...
  <order>
    <name>Камелия японская</name>
    <quantity>20</quantity>
    <price>39.90</price>
  </order>
  <order>
    <name>Гинкго Билоба</name>
    <price>19.90</price>
  </order>
  ...
</order-list>

Пример 1.  Заказ

Начальная задача — создать преобразование, которое бы вычисляло полную стоимость заказанных растений (сложенные по всем заказам произведения количества на цену экземпляра). Эту задачу можно выполнить при помощи XSLT, рекурсивно применяя именованные шаблоны с параметрами.


  1 <?xml version="1.0"?>                                      (1)
    <stx:transform version="1.0"                               (2)
      xmlns:stx="http://stx.sourceforge.net/2002/ns"
      xmlns="http://www.w3.org/1999/xhtml">
  5 
      <stx:variable name="sum" select="0" />                   (3)
      
      <stx:template match="order-list">                        (4)
        <html>                                                 (5)
 10       <body>
            <table>
              <tr>
                <th colspan="5">Заказанные растения</th>
              </tr>
 15           <tr>
                <td>№</td>
                <td>Название</td>
                <td>Количество</td>
                <td>Цена</td>
 20             <td>Итог</td>
              </tr>
              <stx:process-children />
              <tr>
                <td colspan="4">ИТОГО:</td>
 25             <td><b><stx:value-of select="$sum"/></b></td> (6)
              </tr>
            </table>
          </body>
        </html>
 30   </stx:template>
    
      <stx:variable name="name" />
      <stx:variable name="quantity" />
      <stx:variable name="price" />
 35   
      <stx:template match="order">
        <stx:assign name="quantity" select="1" />
        <stx:process-children />
        <stx:assign name="sum"
 40       select="$sum + $quantity * $price" />                (7)
        <tr>
          <td><stx:value-of select="position()" /></td>
          <td><b><stx:value-of select="$name"
/></b></td>
          <td><stx:value-of select="$quantity" /></td>
 45       <td><stx:value-of select="$price" /></td>
          <td><stx:value-of
            select="$quantity * $price" /></td>
        </tr>
      </stx:template>
 50 
      <stx:template match="name">
        <stx:assign name="name" select="." />
      </stx:template>
    
 55   <stx:template match="quantity">
        <stx:assign name="quantity" select="." />
      </stx:template>
    
      <stx:template match="price">
 60     <stx:assign name="price" select="." />
      </stx:template>
    
    </stx:transform>

Пример 2. STX-преобразование для вычисления полной стоимости

Здесь можно взять XML-текст этого преобразования, а здесь — результат его работы в виде аккуратно отформатированного HTML.

Результат работы преобразования, как и ожидалось, — это XHTML-документ, в котором в таблицу сведена информация обо всех заказах, и вычислена их полная стоимость.

Выполнение примера при помощи Joost

Чтобы выполнить преобразование при помощи Joost в среде JDK 1.4 (SAX-парсер, входящий в комплект JDK 1.4, — а это одна из версий Crimson, — далёк от совершенства; в качестве универсального XML-парсера лучше использовать Ælfred2 или Xerces-J2), загрузите дистрибутивы Joost и log4j, и затем выполните строку:


java -cp joost.jar;log4j-1.2.7.jar net.sf.joost.Main plants.xml order-sum.stx

Пример 3. Запуск преобразования при помощи Joost в среде JDK 1.4

Преобразование должно завершиться почти мгновенно: на обычном ПК производительность Joost при выполнении приведённого здесь преобразования составляет около 1 МБ/сёк (эта оценка получена при обработке примера из 250 000 заказов).

В среде JDK 1.3 дополнительно потребуется совместимый с JAXP SAX-парсер, установленный в classpath.

Выполнение примера при помощи STX::XML

Чтобы выполнить преобразование при помощи STX::XML в среде Perl, установите этот модуль и необходимые ему Perl-модули из CPAN: XML::SAX и XML::NamespaceSupport. Также рекомендуется установить модули XML::SAX::Expat и XML::SAX::Writer, хотя это необязательно. Затем выполните скрипт stxcmd.pl, расположенный в каталоге, куда установлен STX::XML:


perl stxcmd.pl order-sum.stx plants.xml

Пример 4. Запуск преобразования при помощи STX::XML в среде Perl

Сходства с XSLT

Одного взгляда на наше преобразование достаточно, чтобы заметить его определённые сходства с XSLT:

(1) STX-преобразования являются синтаксически корректными XML-документами, и значит, их можно редактировать в любом текстовом или XML-редакторе.
(5) STX-преобразования состоят из перемежающихся инструкций и объявлений, относящихся к пространству имён STX, и элементов, переносимых в результат без обработки (литералов).

Усугубляет это сходство тот факт, что имена многих элементов в пространстве имён STX (http://stx.sourceforge.net/2002/ns) совпадают с именами в пространстве имён XSLT 1.0 http://www.w3.org/1999/XSL/Transform); что более важно, эти элементы выполняют те же функции, что и соответствующие им элементы XSLT 1.0.

(2) Преобразование заключено в элементе transform. (STX не поддерживает синтаксис, описанный в разделе 2.3 спецификации XSLT 1.0: «литерал в качестве преобразования».)
(3) В STX поддерживаются как глобальные, так и локальные переменные. Однако переменные в STX и XSLT 1.0 различаются по своему характеру.
(4) В STX для управления ходом преобразования используются декларативные шаблоны, сопоставляемые с документом по определённым правилам. (Эти правила в STX и XSLT 1.0 различаются в нескольких отношениях, как будет объяснено далее.)
(6) В STX есть элемент value-of, помещающий в результат значение выражения.
(7) В STX для выражений используется нестрого типизированный синтаксис, основанный на XPath 2.0; в основном этот синтаксис похож на синтаксис XPath 1.0, используемый в выражениях в XSLT 1.0.

Кроме этого, в STX есть элементы choose, if, param и with-param, которые совпадают по назначению с одноимёнными элементами в XSLT 1.0.

Различия между STX и XSLT 1.0

Узлы и события

Самое важное различие между XSLT и STX — это различие между узлами в XPath 1.0 и событиями в SAX. Основной и неделимый объект, с которым работает XPath 1.0 — узел, существующий в контексте содержащего его документа и отношений с другими узлами (родители, дети и т.д.) И напротив, основной объект, с которым работает SAX — событие, существующее вне всякого дополнительного контекста.

Программирование на уровне событий SAX нетривиально; обычно оно требует отслеживания для каждого события его контекста, сопоставление событию обработчика в соответствии с этим контекстом, и только затем — собственно обработку. STX реализует для потока событий SAX тонкую контекстную оболочку, и в результате узлы, поддерживаемые STX, оказываются достаточно мощными, чтобы могло работать множество полезных XPath-выражений, образующее собственный язык «STXPath», — и для этого не требуется создавать никакую иерархическую структуру наподобие DOM. Контекст узла в STXPath включает в себя:

  1. Текущий узел. Это текущее событие и вся его внутренняя информация; STX сам объединяет последовательные события characters() в один узел.

  2. Цепочка предков. Она состоит из всех предков текущего узла — т.е. из его самого и всех событий startElement(), для которых ещё не было соответствующего события endElement(), — вместе с их контекстами.

  3. Счётчик позиции. Он отслеживает положение текущего узла среди других узлов с тем же родителем, учитывая их тип и части имён (локальное имя, префикс, и полное имя). Значение этого счётчика возвращается функцией position().

  4. Заглядывание вперёд. STX всегда заглядывает на один узел вперёд, чтобы включить в контекст текущего элемента значение следующего за ним события characters() (если оно есть). Это значение становится значением текущего узла.

stx:process-* в сравнении с xsl:apply-templates

В STX нет аналога инструкции XSLT apply-templates, потому что события SAX поступают в STX в том порядке, в котором они расположены в документе: STX не позволяет произвольно выбирать узлы из объектной модели. Вместо этого в STX есть несколько различных инструкций process-*, управляющих обработкой следующих за ними событий SAX (с необязательным указанием group="…"; группы рассматриваются ниже).

[STX process-* schematic]

Рисунок 1. Схема работы process-*

Можно представлять себе инструкции process-* как клапаны: они пропускают определённые события «сквозь себя» в другую часть преобразования; после того, как все пропущенные события будут обработаны, управление вернётся в текущий шаблон.

  1. Инструкция process-children заставляет STX получить и обработать все дочерние узлы текущего узла (если они есть), и затем возвращает управление.

  2. Инструкция process-attributes заставляет STX обработать все атрибуты текущего узла (если они есть).

  3. Инструкция process-siblings заставляет STX обработать все узлы с тем же родителем, что текущий узел; допускаются необязательные выражения while="…" и until="…", задающие дополнительные условия возвращения управления. (При этом дочерние узлы исходного узла и все обработанные узлы с тем же родителем будут забыты, потому что STX не может возвращаться к уже обработанным узлам.) Вместе с возможностью раздельного создания событий startElement() и endElement() (см. ниже), это обеспечивает мощные возможности по переупорядочению содержимого XML-документов.

  4. Инструкция process-self заставляет STX обработать текущий узел повторно — возможно, с использованием другой группы шаблонов.

Переменные в STX

В отличие от XSLT, не допускающего побочных эффектов, переменным в STX можно присваивать новые значения безо всяких ограничений — точно так же, как переменным в обычных языках программирования, таких как Java, C, Python и Perl. В нашем примере эта возможность использовалась для накопления суммы в ходе выполнения преобразования, а также для сохранения в переменных name, quantity и price значений, относящихся к каждому из заказов по очереди.

Уникальные отличительные черты STX

В нашем примере STX-преобразования можно увидеть некоторые сходства и различия между STX и XSLT 1.0; однако в STX есть масса дополнительных возможностей.

Заключение

Мы показали, что STX — это доступное средство для потоковых XML-преобразований. У этого языка уже есть две работоспособные реализации и активное сообщество разработчиков.



XML.com Copyright © 1998-2007 O'Reilly Media, Inc.
Перевод: xmlhack.ru Copyright © 2000-2007 xmlhack.ru