+
" будет вести себя по-разному на строковых и числовых операндах.ПримерРассмотрим выражение
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.xmlText 1
Листинг 12.4. Документ chapter2.xmlText 2
Листинг 12.5. Документ chapter3.xmlText 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