Технология XSLT

Валиков Алексей Н.

Глава 12

Развитие технологий

 

 

Как известно, успех технологии зависит не только от того, насколько продумана и проработана она была. Ее широкое распространение невозможно без поддержки и заинтересованности ведущих производителей программного обеспечения. В этом смысле XSLT очень повезло: имплементациями языка с самых ранних черновых вариантов занимались такие крупные разработчики, как Microsoft, Oracle, IBM, Adobe, Lotus и многие другие. Поддержка Apache XML Project помогла XSLT завоевать популярность и среди open-source сообщества (open-source — разработки с "открытым" исходным кодом).

Так или иначе, сейчас следует лишь констатировать стабильный рост популярности XSLT. Количество XSLT-процессоров уже исчисляется десятками, а число разработчиков — пожалуй, что и тысячами.

Повышенный интерес помог в чрезвычайно короткий срок (менее года) изучить на практике недостатки и достоинства нового языка и приступить к разработке последующих версий, которые бы учитывали эти практические результаты. В декабре 2000 года была выпущена версия 1.1,. в которой было не только исправлено большинство основных проблем первой версии XSLT, но и включены очень важные дополнения — такие, например, как определение интерфейсов расширений для языков Java и JavaScript/ECMAScript. В августе 2001 года версии 1.1 дали статус Final Draft и положили на полку — она никогда не будет стандартом (технической рекомендацией Консорциума W3).

Нужно сказать, что по количеству доработок и дополнений версия XSLT 1.1 могла вполне претендовать на роль нового стандарта XSLT. Однако, в такой напряженной области информационных технологий, как XML, приходится считаться с другими разработками, ибо все они взаимосвязаны. На решение прекратить продвижение XSLT 1.1 и перейти к 2.0 во многом повлияли такие проекты, как XML Schema и XQuery.

XML Schema — это долгожданный XML-язык, описывающий структуру XML-документа, своего рода более мощный вариант DTD. XML Schema, в частности, позволяет описывать простые и сложные типы данных элементов и атрибутов, ограничивать количества повторений, определять в XML-документах первичные и внешние ключи и многое другое. Помимо этого, XML Schema определяется в XML-синтаксисе, что позволяет использовать для обработки схем стандартные XML-инструменты. Спецификация XML Schema получила статус технической рекомендации Консорциума W3 в мае 2001 года.

XQuery — это текущий проект W3C по созданию языка запросов для XML-документов. В основу XQuery легло множество предыдущих исследований в области языков запросов для полуструктурированных данных — пожалуй, стоит упомянуть такие, как Quilt, XML-QL и Lorel. Почти все старые языки запросов для XML были университетскими исследовательскими проектами; в XQuery же заинтересованы такие гиганты, как Microsoft и Software AG.

Следует пояснить, каким образом XML Schema и XQuery влияют на XSLT — казалось бы, их области применения несколько различаются. Напомним, что весомая часть функциональности XSLT зависит от языка XPath, который используется также и в XPointer. Как оказалось, XPath важен не только для XSLT и XPointer, но и для XQuery. Модель XML-документа, описанная в первой версии XPath, оказалась мощной, легко реализуемой и понятной абстракцией физической сущности XML и поэтому ее было решено использовать также и в XQuery. В следующей своей инкарнации эта модель будет выделена в отдельную спецификацию — "XQuery 1.0 and XPath 2.0 Data Model" ("Модель данных XQuery 1.0 и XPath 2.0"). Функции и операторы также будут выделены в отдельный документ — "XQuery 1.0 and XPath 2.0 Functions and Operators Version 1.0" ("Операторы и функции в XQuery 1.0 и XPath 2.0, версия 1.0").

Принятие XML Schema также оказывает определенное влияние на XPath. В схемах ХМL-документов можно определять типы данных атрибутов и элементов. Соответственно, семантика XPath выражений должна отражать эту метаинформацию: например, оператор сложения "+" будет вести себя по-разному на строковых и числовых операндах.

Пример

Рассмотрим выражение int/x + int/y на простейшем документе:

 2

 2

В первой версии XPath результатом вычисления int/x + int/у в любом случае будет 4. Между тем, старшие версии могут учитывать метаинформацию о типе обрабатываемых данных и возвращать 4 в случае числовых операндов и "22" в случае строковых.

На момент написания этих строк работа над XSLT 2.0 и XPath 2.0 идет полным ходом. Конечно, пока еще рано заглядывать вперед и раскрывать секреты рабочей группы XSL, однако, основываясь на опубликованных спецификациях XLST 1.1 и требованиях к версии XSLT 2.0, кое-какие выводы сделать все же можно.

 

Отличия XSLT 1.1 от XSLT 1.0

 

Отсутствие result tree fragment

Главное и наиболее существенное отличие XSLT 1.1 от XSLT 1.0 состоит в том, что тип данных, известный в XSLT 1.0 как result tree fragment (результирующий фрагмент дерева) в XSLT 1.1. отсутствует. Вместо него в версии 1.1 используется множество узлов, состоящее из единственного корневого узла результирующего фрагмента.

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

Пример

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

 #0E0E0E

 #FFFFFF

создает в переменной colors результирующий фрагмент дерева. В соответствии со спецификацией XPath 1.0 выражение $colors/color[1] будет некорректным, поскольку типом colors является результирующий фрагмент дерева, который не может быть напрямую преобразован во множество узлов. Иными словами, совершенно логичное и оправданное выражение не является корректным. Конечно, существуют способы обойти этот запрет — с помощью расширений и тому подобного, но нельзя не согласиться с тем, что результирующие фрагменты являются самой большой занозой в XSLT 1.0.

XSLT 1.1 исправляет этот просчет. Переменная colors, определенная выше, будет иметь своим значение не фрагмент дерева, а множество из одного, корневого, узла этого фрагмента и ее можно использовать везде, где только можно использовать тип данных node-set.

 

Несколько выходящих документов

Как известно, преобразование в XSLT 1.0 имеет один основной входящий документ (плюс документы, доступные при помощи функции document) и ровно один выходящий документ. То есть, для того, чтобы сгенерировать на основе одного входящего документа несколько выходящих следует просто выполнить несколько преобразований.

Следуя многочисленным запросам программистов, почти все разработчики XSLT-процессоров предоставили в своих продуктах возможность генерировать несколько выходящих документов непосредственно из одного преобразования. Элемент xsl:document, добавленный в XSLT 1.1, сделал эту возможность стандартной.

Пример

Самым простым применением xsl:document является разбиение одного документа на несколько. Например, имея документ вида

 Text 1

 Text 2

 Text 3

мы можем выделить элементы chapter в отдельные файлы, а в самом выходящем документе создать оглавление со ссылками.

Листинг 12.1. Преобразование, использующее элемент xsl:document

 version="1.1"

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

 

  

 

 

 

 

 

  

  

 

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

Листинг 12.2. Главный выходящий документ преобразования

 

 

 

Листинг 12.3. Документ chapter1.xml

Text 1

Листинг 12.4. Документ chapter2.xml

Text 2

Листинг 12.5. Документ chapter3.xml

Text 3

 

Дополнительные возможности по расширению

В XSLT 1.1 был введен элемент xsl:script, предоставляющий дополнительные возможности для создания и использования функций расширения. При помощи xsl:script функции расширения могут быть явным образом определены в самом преобразовании.

Пример

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

Листинг 12.6. Преобразование, включающее функцию расширения

 version="1.1"

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

 xmlns:js="javascript:code">

 

  function iff(arg1, arg2, arg3) {

   if (arg1) {

    return arg2;

   } else {

    return arg3;

   }

  }

 

 ...

Атрибут implements-prefix (англ. implements prefix — реализует префикс) связывает определяемую функцию с некоторым пространством имен (как мы отмечали ранее, все функции расширения должны принадлежать ненулевым пространствам имен). При вызове функций из этого пространства имен в XPath-выражениях, процессор будет искать их определения в элементах xsl:script, которые реализуют соответствующий префикс.

Атрибут language определяет язык программирования, в котором написано расширение. Очевидно, язык влияет на то, как будет выполняться расширение — например, должен ли процессор интерпретировать содержимое xsl:script или следует загрузить внешний Java-класс. Естественно, не следует ожидать, что любой процессор сможет выполнять расширения, написанные на произвольных языках программирования — как правило, разработчики XSLT-средств в документации к своим продуктам оговаривают, какие языки расширения они поддерживают. Как следствие, преобразование, использующее расширения, написанные на "непонятном" процессору языке, либо не будут выполнены вообще, либо будут выполнены некорректно.

Помимо двух обязательных атрибутов implements-prefix и language, в элемент xsl:script могут быть включены атрибуты src и archive, которые указывают физическое местоположение кода расширения.

 

"Внешние" типы данных

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

В XSLT 1.1 была впервые представлена поддержка произвольных внешних типов данных. Функции расширения могут возвращать и оперировать любыми типами данных. Например, в XSLT-процессорах, написанных на Java, в случае использования расширений в качестве значений часто используются произвольные классы.

Пример

Форматирование текущей даты и времени, которое было продемонстрировано в главе 10 элементом ext:date, может быть переписано при помощи функций расширения следующим образом.

Листинг 12.7. Использование внешних типов данных в преобразовании

 version="1.1"

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

 xmlns:Date="java:java.util.Date"

 xmlns:SimpleDateFormat="java.text.SimpleDateFormat">

 

 

 

 

 

Пространства имен с префиксами Date и SimpleDateFormat определяют привязку к Java-классам java.util.Date и java.text.SimpleDateFormat соответственно (в этом примере мы используем формат URI пространств имен, принятый в процессоре Saxon).

Объявление

присваивает переменной df результат выполнения конструктора класса SimpleDateFormat со строковым параметром "HH:mm", что эквивалентно Java-коду

SimpleDateFormat df = new SimpleDateFormat("НН:mm");

Иными словами, переменной df был присвоен "внешний" тип данных java.text.SimpleDateFormat. Аналогично, переменная now содержит данные типа java.util.Date. Фактически, этим переменным были присвоены экземпляры соответствующих классов.

Выражение SimpleDateFormat:format($df, $now), использованное в этом преобразовании, представляет собой ни что иное, как применение метода format экземпляра класса SimpleDateFormat, присвоенного переменной df к экземпляру класса Date, присвоенного переменной now. В переводе на Java:

df.format(now);

Надо сказать, что оперирование внешними типами — отнюдь не нововведение XSLT 1.1. Во многих процессорах интерфейсы расширения позволяют функциям возвращать произвольные типы данных. Важно, что теперь эта возможность закреплена в официальном документе Консорциума W3, и следует полагать, что и из второй версии языка она никуда не денется.

 

Стандартные интерфейсы расширений

Важным дополнением в XSLT 1.1 по сравнению с первой версией языка является определение стандартных интерфейсов расширения для языков IDL, JavaScript/ECMAScript и Java на основе интерфейсов DOM2.

Одна из проблем, с которыми всегда приходится сталкиваться при работе с расширениями, является проблема переносимости. Вследствие того, что интерфейсы привязки к конкретным языкам программирования отдали в первой версии на усмотрение разработчиков процессоров, несовместимость интерфейсов не позволяет гарантировать работоспособность расширений при переходе с одного процессора на другой (даже если речь идет о процессорах одного типа, например, написанных на языке Java процессорах Saxon, Xalan и Oracle XSLT Processor).

Ситуация, действительно, довольно досадная. С одной стороны, и XSLT, и Java являются переносимыми языками, с другой стороны, их сочетание в случае использования расширений оказывается непереносимым даже на Java-платформах. Стандартные интерфейсы, выработанные в XSLT 1.1, по всей вероятности, намного упростят положение вещей — ведь если написанное единожды Java-расширение будет работать на всех Java-платформах, этого уже будет достаточно, для того чтобы смело использовать всю мощь расширений.

 

Другие изменения

Помимо приведенных выше отличий версии 1.1 от первой версии языка, новый вариант включает в себя также некоторые другие добавления и исправления:

□ добавлена расширенная поддержка пространств имен при преобразовании;

□ добавлена поддержка XML Base;

□ добавлена возможность использования параметров при вызове шаблонов элементом xsl:apply-imports;

□ расширено множество атрибутов элементов XSLT, которые могут содержать шаблоны значений атрибутов;

□ добавлено определение лексикографического порядка (наподобие 'а' < 'b' → true);

□ добавлено сравнение строк без учета регистра символов;

□ добавлены операторы для проверки порядка следования узлов в документе;

□ исправлены обнаруженные ошибки.

 

Отличия XSLT 2.0 от XSLT 1.1

 

Прежде чем приступить к описанию отличий второй версии XSLT от версии 1.1 (и, соответственно, 1.0), следует сделать одно существенное замечание. Лицензионные соглашения Консорциума W3 не позволяют раскрывать широкой общественности внутренние материалы рабочих групп W3C до того, как они будут официально опубликованы. Потому, строго говоря, все, что будет ниже сказано о версии 2.0 — это не более чем совокупность гипотез, пожеланий и выводов, сделанных на основе спецификации XSLT 1.1 и требований к XSLT 2.0 и XPath 2.0. Эти документы доступны публично.

 

Изменения в XPath 2.0

Разрабатываемая версия языка XPath, вследствие интеграции с XQuery, очевидно, претерпит серьезные изменения. Новая спецификация уже сейчас разбита на два документа: документ, описывающий модель данных и документ, описывающий функции и операторы. Поэтому на данный момент сложно делать точный прогноз относительно того, что же получится в итоге. Мы ограничимся перечислением основных требований:

□ поддержка группы XML-стандартов: определение модели в терминах XML Information Set, выделение общего синтаксиса и семантики с XQuery 1.0;

□ переопределение операторов сравнения на множествах;

□ определение операторов пересечения и разности множеств;

□ расширение множества агрегатных функций (наподобие sum, count, min, max — функций, работающих на множествах);

□ возможность использования выражений, возвращающих множества узлов в качестве шагов выборки, например /a/(b|c)/d вместо /а/b/d | /a/c/d;

□ введение оператора аналогичного оператору ? в Java и С (выражение a ? b : с, где а имеет булевый тип, возвращает b, если a — "истина" и с, если a — "ложь");

□ дополнительные строковые функции как-то: замена подстроки, выравнивание, изменение регистра символов;

□ поддержка регулярных выражений;

□ поддержка примитивных типов XML Schema;

□ использование информации о структуре документа, определенной в его схеме;

□ поддержка экспоненциальной нотации чисел (наподобие 2Е10 = 1024);

□ поддержка функций приведения и преобразования (аналог CAST и CONVERT из SQL).

 

Выбор шаблонов для элементов пространства имен, определенного по умолчанию

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

 ...

то обрабатывать он будет только те элементы foo, которые принадлежат нулевому пространству имен. Для элемента

придется писать шаблон вида

 ...

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

Листинг 12.8. Изменение пространства имен для паттерна

 version="2.0"

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

 xmlns="urn:bar-namespace">

 

  ...

 

В элементе xsl:stylesheet пространство имен с URI "urn:bar-namespace" определяется как пространство имен по умолчанию и паттерн foo соответствует элементу с локальной частью имени "foo" и URI пространства имен "urn:bar-namespace".

 

Средства для форматирования даты и времени

В первых версиях XSLT элемент xsl:decimal-format и функция format-number обеспечивали форматирование чисел при их текстовом отображении. К сожалению, подобных инструментов для форматирования даты предусмотрено не было.

Поскольку связка элемента, определяющего именованный формат и функции, выполняющей форматирование, оказалась очень удачной, по всей вероятности, подобную схему мы будем наблюдать и в версии 2.0. Скорее всего, связка для форматирования даты и времени будет состоять из элемента xsl:date-format и функции format-date.

 

Функции

id

и

key

на внешних документах

В XSLT 1.1 функции id и key возвращают множества узлов документа, который содержит текущий узел преобразования. То есть для того, чтобы использовать ключи или уникальные идентификаторы для выбора узлов внешнего документа, необходимо сначала сменить контекст, например:

 

Требования ко второй версии XSLT предполагают упрощение работы с ключами и уникальными идентификаторами на внешних документах.

 

Включение неразбираемых внешних сущностей в виде текста

В первых версиях XSLT отсутствовала возможность включения внешних сущностей, не разбирая их как XML-документы. Без помощи расширений было невозможно включить в выходящий документ простой внешний текстовый файл. Между тем, вполне подходящим решением была бы функция типа unparsed-entity, которая по данному URI возвращала бы содержимое ресурса в виде строки. Естественно, при этом необходимо учитывать кодировку внешней сущности и Unicode-символы, которые не могут присутствовать в XML (например, управляющие символы).

 

Использование именованных сущностей вместо кодов символов

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

&nbsp;

совсем не гарантирующим, кстати, что в выходящем файле окажется именно  .

В самом преобразовании сущности можно определять в DTD-заголовке следующим образом:

 

]>

 ...

Однако на выходящий документ эти определения никоим образом не сказываются.

 

Обращение ссылок по

ID

/

IDREF

Функция id позволяет отыскать в документе элементы по заданным значениями ID-атрибутов. Это особенно полезно при работе с IDREF-атрибутами, которые ссылаются на ID-атрибуты: можно с легкостью выбрать элементы, на которые ссылается текущий элемент. Новым требованием к XSLT 2.0 является возможность "обращать" такого рода ссылки — то есть находить элементы, которые ссылаются на данный элемент (включают определенные значения в свои IDREF-атрибуты).

 

Другие требования

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

□ поддержка группировки;

□ поддержка Unicode-нормализации строк;

□ сортировка узлов в соответствии с информацией о их типах, сообщенной XML-схемой документа;

□ создание и копирование узлов с учетом информации об их типах;

□ создание пространства имен с вычисляемым префиксом и URI.