Технология XSLT

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

Глава 4

Структура преобразования

 

 

Пространство имен XSLT

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

Уникальный идентификатор ресурса пространства имен XSLT имеет вид

http://www.w3.org/1999/XSL/Transform

Как отмечалось ранее, по адресу, указанному в URI пространства имен, совершенно необязательно будет находиться что-либо осмысленное. Однако в нашем случае по адресу http://www.w3.org/1999/XSL/Transform находится текстовый документ, содержащий единственную строчку:

This is the XSLT namespace.

Символ 1999 в URI пространства имен XSLT никак не соотносится с версией языка преобразования. Это просто год, который был назначен Консорциумом W3 данной спецификации и не более. Версия использованного языка определяется атрибутом version элемента xsl:stylesheet.

Общепринятым префиксом пространства имен языка XSLT является префикс xsl. Естественно, он может быть любым другим, но в этой книге мы будем использовать именно такое обозначение. Таким образом, объявление пространства имен XSLT в общем случае будет выглядеть следующим образом: xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

Пример

Приведем пример простого преобразования, в котором объявлено пространство имен XSLT.

 version="1.0"

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

 

 

 

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

 version="1.0"

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

 

Кроме этого, пространство имен по умолчанию можно снова обнулить:

 version="1.0"

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

 

В последнем случае элемент root будет принадлежать нулевому пространству имен. Результат всех трех преобразований одинаков:

Элементы XSLT могут содержать атрибуты, принадлежащие другим, но обязательно ненулевым, пространствам имен. Такие атрибуты могут содержать дополнительную информацию, но поскольку они не относятся к XSLT, обрабатываться процессором в общем случае они не будут.

Пример

Если мы определим в преобразовании элемент вида

 xmlns:xsldoc="http://www.a.com/XSL/doc">

 ...

то в общем случае атрибут xsldoc:text будет проигнорирован. Однако процессор, которому знакомо пространство имен с URI http://www.a.com/XSL/doc сможет понять, что этот атрибут применен для документирования преобразования и будет использовать его в своих целях.

 

Корневые элементы преобразования

 

За исключением случаев упрощенных преобразований, корневым элементом XSLT-документа всегда является элемент xsl:stylesheet или его синоним xsl:transform. Эти элементы полностью идентичны и различаются только именами, поэтому мы будем описывать семантику и пользоваться только элементом xsl:stylesheet.

 

Элементы

xsl:stylesheet

и

xsl:transform

 id=" идентификатор "

 extension-element-prefixes=" префиксы "

 exclude-result-prefixes=" префиксы "

  version =" число ">

 

 extension-element-prefixes=" префиксы "

 exclude-result-prefixes=" префиксы "

  version =" число ">

 

Элемент xsl:stylesheet имеет обязательный атрибут version, в котором указывается версия языка, использованная при создании этого преобразования. Текущей версией языка является версия 1.0, поэтому все преобразования, которые мы будем приводить в качестве примеров, будут начинаться следующим тегом:

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

Пример

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

...

 version="1.0"

 id="trans"

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

 ...

...

то ассоциироваться с документом оно будет следующей инструкцией:

Необязательный атрибут extension-element-prefixes перечисляет префиксы пространств имен, которые определяют элементы расширения. Об использовании этого атрибута мы расскажем в главе 10, которая посвящена созданию расширений языка XSLT.

Необязательный атрибут exclude-result-prefixes перечисляет префиксы пространств имен, определения которых не нужно включать в выходящий документ. Использование этого атрибута подробно описано в главе 8.

Элемент xsl:stylesheet может включать следующие элементы языка XSLT:

□ xsl:import;

□ xsl:include;

□ xsl:strip-space;

□ xsl:output;

□ xsl:key;

□ xsl:decimal-format;

□ xsl:namespace-alias;

□ xsl:attribute-set;

□ xsl:variable;

□ xsl:param;

□ xsl:template.

Эти элементы называются элементами верхнего уровня, поскольку они могут находиться на самом верхнем (не считая уровня корневого элемента) уровне в иерархии элементов документа. Более того, все перечисленные элементы кроме xsl:variable и xsl:param должны находиться только на верхнем уровне. Элементы xsl:variable и xsl:param могут использоваться в шаблонах, определяя локальные переменные и параметры.

Если преобразование импортирует внешние модули, первыми дочерними элементами xsl:stylesheet должны быть элементы xsl:import. Иначе говоря, элементам xsl:import внутри xsl:stylesheet должны предшествовать только другие элементы xsl:import. Порядок всех остальных дочерних элементов xsl:stylesheet не имеет значения.

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

Пример

Листинг 4.1. Преобразование с элементом верхнего уровня, не принадлежащим XSLT

 version="1.0"

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

 

  Simple stylesheet

 

 

 

 

Выделенный полужирным шрифтом на листинге 4.1 элемент source принадлежит пространству имен с URI http://www.a.com/XSL/source. Поскольку пространство имен этого элемента ненулевое, такое объявление является корректным.

 

Упрощенные преобразования

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

Листинг 4.2. Простое преобразование

 version="1.0"

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

 

 

  

 

 

XSLT позволяет упрощать запись таких преобразований, опуская элементы xsl:stylesheet и xsl:template и оставляя только шаблон, создающий выходящий документ.

Корневой элемент упрощенной записи должен содержать атрибут xsl:version, указывающий версию языка XSLT, использованного в шаблоне. Как правило, этот элемент также содержит объявление пространства имен XSLT, хотя оно может быть определено и в другом месте.

Пример

Преобразование, приведенное в листинге 4.2, можно переписать в упрощенном виде следующим образом.

Листинг 4.3. Упрощённая запись преобразования

 xsl:version="1.0"

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

 

Приведем еще один простой пример упрощенной записи преобразования, генерирующего простейшую HTML-страницу.

Листинг 4.4. Упрощённая запись преобразования XML-документа в HTML

 

  </p> <p class="paragraph">    <xsl:value-of select="page/name"/> </p> <p class="paragraph">  

 

 

 

 

Следующий листинг приводит полную версию этого же преобразования.

Листинг 4.5. Полная запись преобразования XML-документа в HTML

 version="1.0"

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

 

 

  

    </p> <p class="paragraph">      <xsl:value-of select="page/name"/> </p> <p class="paragraph">    

  

  

   

  

 

 

 

Модульная организация преобразования

 

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

 

Включение преобразований

Подобно тому, как мы бы использовали в языке С директиву #include для включения внешних файлов, преобразования в XSLT могут использовать для той же самой цели элемент xsl:include. Правда, в отличие от языка С, условное включение в XSLT невозможно.

 

Элемент

xsl:include

  href = " URI "/>

Обязательный атрибут href элемента xsl:include содержит URI внешнего модуля, который должен быть включен в текущее преобразование. Внешний модуль обязан быть корректным XSLT-преобразованием.

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

Пример

Рассмотрим простое преобразование a.xsl, которое определяет значение переменной date.

Листинг 4.6. Преобразование a.xsl

 version="1.0"

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

 

Включим a.xsl в преобразование b.xsl.

Листинг 4.7. Преобразование b.xsl

 version="1.0"

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

 

 

 

   Today is

  

   .

 

 

Включение в преобразование b.xsl преобразования a.xsl эквивалентно замене в b.xsl соответствующего элемента xsl:include на содержимое преобразования a.xsl. В нашем случае будет включено только определение переменной date. Преобразование b.xsl можно переписать в следующем виде: .

 version="1.0"

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

 

 

  

   Today is

  

   .

 

 

При включении внешних преобразований при помощи xsl:include следует учитывать некоторые особенности использования этого элемента.

Все ссылки и относительные идентификаторы ресурсов (URI), используемые во включаемом преобразовании, вычисляются относительно его базового адреса.

Пример

Предположим, что URI нашего преобразования имеет вид:

http://www.xsltdev.ru/examples/a.xsl

В этом случае элемент

будет включать преобразование с URI

http://www.xsltdev.ru/examples/b.xsl

Нет никаких проблем и с включением преобразований по абсолютным идентификаторам. Например, если преобразование identity.xsl находится по адресу

http://www.xsltdev.ru/stylesheets/identity.xsl

то включить его можно элементом

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

В XSLT элементы xsl:import всегда должны быть первыми дочерними элементами головного элемента xsl:stylesheet. Поэтому элементы xsl:import внешнего преобразования включаются сразу после элементов xsl:import основного преобразования. Если в основном преобразовании элементов xsl:import нет, то включаемые элементы xsl:import становятся первыми дочерними элементами xsl:stylesheet основного преобразования.

Пример

Предположим, что в основное преобразование мы импортируем файл a.xsl и включаем файл b.xsl.

Листинг 4.8. Основное преобразование

 version="1.0"

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

 

 

 

 

Листинг 4.9. Преобразование b.xsl

 version="1.0"

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

 

 

Тогда основное преобразование может быть переписано следующим образом.

Листинг 4.10. Основное преобразование после включения b.xsl

 

 

 

 

Элемент xsl:include можно использовать и для включения преобразований с упрощенным синтаксисом. Преобразования такого рода будут включаться как эквивалентные им преобразования стандартного синтаксиса — то есть с корневым элементом xsl:stylesheet и единственным шаблоном, соответствующим корневому узлу.

Пример

Предположим, что мы используем преобразование упрощенного синтаксиса simple.xsl.

Листинг 4.11. Преобразование simple.xsl

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

 

Включим simple.xsl в основное преобразование.

Листинг 4.12. Основное преобразование

 version="1.0"

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

 

 

 

 

Тогда основное преобразование может быть переписано в следующем виде.

Листинг 4.13. Основное преобразование после включения simple.xsl

 version="1.0"

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

 

 

  

 

 

 

 

 

Полужирным шрифтом на листинге 4.13 выделен шаблон, который соответствует преобразованию simple.xsl.

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

Включаемые модули являются полноценными и самостоятельными преобразованиями. К примеру, они также могут включать другие преобразования при помощи тех же элементов xsl:include. При этом преобразование не должно прямо или косвенно включать само себя — такая ситуация породит бесконечный цикл включений.

 

Импорт преобразований

Другим способом использования внешних модулей в XSLT является импорт преобразований, который обеспечивается элементом xsl:import. Импорт преобразований более сложен, чем их простое включение — последовательность импорта модулей может влиять на то, как будет выполняться преобразование. Равно как и в случае с xsl:include, условное импортирование преобразований не разрешено.

 

Элемент

xsl:import

 

  href =" URI "/>

Синтаксис импорта преобразования практически полностью аналогичен включению: обязательный атрибут href содержит URI внешнего модуля, который должен быть импортирован в текущее преобразование. Так же, как и в случае с xsl:include, элемент xsl:import логически заменяется содержимым внешнего модуля, и относительные идентификаторы ресурсов (URI), используемые во внешнем преобразовании, отсчитываются от его базового адреса. Преобразование не может прямо или косвенно импортировать само себя.

Совет

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

Главным отличием импорта преобразований является то, что последовательность импортирования внешних модулей, называемая порядком импорта оказывает влияние на приоритет исполнения шаблонов, определения и многое другое.

 

Порядок импорта

Как уже было сказано выше, элементы xsl:import должны всегда быть первыми дочерними элементами xsl:stylesheet. Порядок, в котором они находятся в преобразовании, определяет порядок импорта внешних модулей следующим образом.

□ Порядок импорта основного преобразования всегда старше порядка импорта внешнего преобразования.

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

□ Порядок импорта преобразования, включенного в основное при помощи элемента xsl:include, равен порядку импорта основного преобразования.

Эти правила могут быть проиллюстрированы следующими примерами.

Рассмотрим преобразование alpha.xsl, которое импортирует преобразования bravo.xsl и сharlie.xsl и включает преобразование delta.xsl.

Листинг 4.14. Фрагмент преобразования alpha.xsl

 version="1.0"

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

 

 

 

 

В соответствии с первым правилом, порядок импорта основного преобразования старше порядка импорта внешних модулей, значит alpha.xsl старше bravo.xsl и charlie.xsl. Далее, согласно второму правилу порядок импорта преобразования bravo.xsl младше порядка charlie.xsl, поскольку оно импортируется первым. Преобразование delta.xsl будет иметь порядок импорта такой же, как и у основного преобразования alpha.xsl. Таким образом, порядок импорта в этом примере будет иметь следующий вид:

bravo.xsl

charlie.xsl

alpha.xsl delta.xsl

Преобразование bravo.xsl будет самым младшим, а преобразования alpha.xsl и delta.xsl — самыми старшими.

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

Техническая рекомендация XSLT предлагает решать эту проблему построением логического дерева импорта.

Пример

Рассмотрим следующую схему включений и импорта (табл 4.1).

Таблица 4.1 . Включение и импорт преобразований

Преобразование Импортирует Включает
alpha.xsl bravo.xsl charlie.xsl
bravo.xsl delta.xsl echo.xsl foxtrot.xsl
charlie.xsl golf.xsl hotel.xsl
hotel.xsl india.xsl

Этой схеме будет соответствовать логическое дерево импорта на рис. 4.1.

Рис. 4.1. Обход дерева импорта преобразований

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

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

delta.xsl

echo.xsl

bravo.xsl foxtrot.xsl

golf.xsl

hotel.xsl india.xsl

charlie.xsl

alpha.xsl

Порядок, в котором импортируются модули, непосредственным образом влияет на различные аспекты преобразования. Эффект, который оказывает порядок импорта на те или иные элементы, будет подробно описан при их рассмотрении — сейчас же мы их просто коротко перечислим.

□ xsl:attribute-set — порядок импорта используется для определения главенства элементов xsl:attribute, включенных в разные именованные списки атрибутов, но создающих атрибуты с одинаковыми именами.

□ xsl:namespace-alias — в случае, если в преобразовании определяются несколько псевдонимов префиксов пространств имен, процессор использует самый старший в порядке импорта псевдоним.

□ xsl:output — эти элементы объединяются процессором. В случае конфликтов, например, когда в разных элементах xsl:output атрибуты определены по-разному, процессор должен использовать старшее в порядке импорта определение.

□ xsl:strip-space и xsl:preserve-space — в этих элементах порядок импорта также используется для разрешения конфликтов: выигрывают определения со старшим порядком импорта.

□ xsl:template — порядок импорта используется для разрешения конфликтов, которые возникают в случаях, когда один узел может быть обработан несколькими шаблонами. Шаблон, содержащийся в преобразовании с младшим порядком импорта, будет просто исключен из рассмотрения.

□ xsl:variable и xsl:param — порядок импорта используется при обращении к глобальным переменным в случае, если в разных преобразованиях существуют разные определения переменной с одним именем. В подобной ситуации будет использована переменная со старшим порядком импорта.

 

Использование сущностей для разбивки на модули

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

Пример

Листинг 4.15. Входящий документ:

 

 

Листинг 4.16. Основное преобразование

 

]>

 version="1.0"

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

 

 

  

 

 

 &ab;

Листинг 4.17. Файл ab.xsl

 

 

Листинг 4.18. Результат преобразования

 

 

В этом примере в DTD-блоке мы определяем сущность с именем ab, которая содержит два шаблонных правила для обработки элементов a и b. Файл ab.xsl, в котором содержится текст внешней сущности, заменяет в документе ссылку &ab;. После раскрытия процессором сущности (замены ссылки на ее содержимое) наше преобразование будет выглядеть следующим образом.

Листинг 4.19. Основное преобразование после раскрытия сущности &ab;

 version="1.0"

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

 

 

  

 

 

 

 

 

 

 

 

 

Совместное использование преобразований и XML-документов

 

Ассоциация преобразования с XML-документом

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

Подобные задачи возникают и при работе с XSLT. Например, если для обработки XML-документа всегда будет использоваться одно и то же преобразование, логично будет закрепить это преобразование за документом.

Для того чтобы закрепить XSLT-преобразование за XML-документом, в последнем должна быть использована инструкция по обработке xml-stylesheet, которая имеет следующий вид:

  href =" URI "

  type =" тип "

 title=" название "

 media=" тип носителя "

 charset=" кодировка "

 alternate="yes" | "no"?>

Заметим, что xml-stylesheet может закреплять за XML-документами не только преобразования. Основным назначением инструкции xml-stylesheet является ассоциация с документом фиксированного стиля (англ. stylesheet — стиль, стилевая таблица). С этой точки зрения преобразования являются не более, чем частным случаем стилевых таблиц.

Инструкция xml-stylesheet содержит шесть псевдоатрибутов (приставка псевдо- поясняет, что на самом деле инструкции по обработке не имеют атрибутов), два из которых, href и type, являются обязательными. Использование псевдоатрибутов xml-stylesheet поясняет табл. 4.2.

Таблица 4.2 . Псевдоатрибуты инструкции по обработке xml-stylesheet

Псевдоатрибут Описание
href Указывает местоположение стиля, закрепляемого за документом. В случае преобразований, href указывает местоположение преобразования, которое нужно применять к этому документу. В псевдоатрибуте href может быть также указан уникальный идентификатор преобразования, если оно включено в сам документ ( см. раздел "Включение преобразования в документ" ).
type Указывает тип стиля, закрепляемого за документом. В нашем случае, поскольку мы ассоциируем с документом XSLT-преобразование, псевдоатрибут type должен иметь значение " text/xsl "
title Задает название закрепляемого стиля. Название не имеет особого значения при обработке — оно просто поясняет назначение стиля
media Указывает тип носителя или устройства, для которого предназначен результирующий документ
charset Определяет кодировку, в которой создан стиль. Если стиль является XSLT-преобразованием, значение псевдоатрибута charset в расчет не принимается, поскольку кодировка преобразований явно или неявно определена в них самих
alternate Указывает, является ли данный стиль основным (" no ") или альтернативным (" yes "). Значением этого атрибута по умолчанию является " no "

Примечание

Что касается псевдоатрибута type , то на самом деле нет стандарта, который заставлял бы использовать значение " text/xsl ". Рабочая группа XSL Консорциума W3 до сих пор обсуждает, какой именно тип должен быть присвоен XSLT. Поскольку XSLT есть XML-язык, формально следовало бы использовать " application/xml ", однако с легкой подачи Microsoft все используют " text/xsl ".

Инструкция xml-stylesheet может быть включена только в пролог документа, то есть она должна предшествовать корневому элементу. Не рекомендуется включать эту инструкцию в блоки DOCTYPE, поскольку некоторые парсеры и процессоры будут ее в этом случае игнорировать.

Примеры

Стандартный механизм использования xml-stylesheet может быть продемонстрирован следующим документом:

 

В этом документе инструкция xml-stylesheet указывает на то, что этот документ должен быть обработан XSLT-преобразованием mytransform.xsl.

Псевдоатрибут title может содержать краткое описание применяемого преобразования:

 title="Generate menu"

 type="text/xsl"

 href="menu.xsl"?>

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

 type="text/xsl"

 href="pda.xsl"

 alternate="yes"

 media="handheld"?>

Теоретически, если документ с такой инструкцией будет показываться на мобильном устройстве (например, на Palm Pilot), он должен быть преобразован при помощи pda.xsl. На практике не следует полагаться на подобные возможности, поскольку они сильно зависят от поддержки серверов и процессоров, которая в этом отношении все еще сильно ограничена.

В заключение описания инструкции xml-stylesheet приведем правила, которые определяют ее синтаксис.

[XMS1] StyleSheetPI    ::= ''

[XMS2] PseudoAtt       ::= Name S? '=' S? PseudoAttValue

[XMS3] PseudoAttValue  ::= ( '"' ([^"<&]|CharRef|PredefEntityRef)* '"'

                           | "'" ([^'<&]|CharRef|PredefEntityRef)* "'")

                           - (Char* '?>' Char*)

[XMS4] PredefEntityRef ::= '"' | '<'

                           | '>' | '&' | '''

 

Объединение документа и преобразования

 

XSLT-преобразование является, как правило, самостоятельным XML-документом, корневым элементом которого является xsl:stylesheet или xsl:transform. Вместе с тем, иногда бывает необходимо объединять преобразуемый документ и само преобразование так, чтобы они находились в одном файле.

Мы опишем два способа объединения документов и преобразований. Первый основывается на использовании инструкции xml-stylesheet для того, чтобы закрепить за документом преобразование, находящееся внутри него самого. Во втором способе обрабатываемый документ включается в преобразование как пользовательский элемент верхнего уровня и обрабатывается при помощи функции document('') с пустым строковым параметром.

 

Включение преобразования в документ

Корневой элемент преобразования xsl:stylesheet может быть включен в преобразуемый документ со всеми дочерними элементами верхнего уровня и так далее. Для того чтобы использовать это преобразование, псевдоатрибут href инструкции по обработке xml-stylesheet должен указывать на идентификатор элемента xsl:stylesheet, определенный в его атрибуте id.

Пример

Листинг 4.20. Входящий документ

 Main page

 Main content

 

  id="transform"

  version="1.0"

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

 

  

   

  

 

 

 

Листинг 4.21. Выходящий документ

 Main content

Поскольку элемент xsl:stylesheet включен в преобразуемый документ, он также подвергнется преобразованию. Для того чтобы избежать этого, в преобразование включается шаблонное правило, которое указывает, что элементы xsl:stylesheet следует игнорировать:

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

 

Включение документа в преобразование

Другой возможностью объединения документов и преобразований является включение элемента документа в преобразование в виде элемента верхнего уровня.

Поскольку преобразование также является XML-документом, доступ к данным, которые оно содержит можно получить при помощи функции document, так же, как если бы документ преобразования был внешним документом. Функция document, которой в качестве параметра была передана пустая строка, возвращает множество, состоящее из корневого узла самого преобразования. То есть, если документ был включен в преобразование в качестве элемента верхнего уровня с именем, к примеру, user:input, получить доступ к нему можно при помощи выражения

document('')/xsl:stylesheet/user:input

Пример

Листинг 4.22. Входящий документ

Листинг 4.23. Преобразование

 version="1.0"

 xmlns:user="urn:user"

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

 exclude-result-prefixes="user">

 

 

 

 

 

 

   select="document('')/xsl:stylesheet/user:input"/>

 

 

 

 

 

 

 

 

 

  

 

 

Листинг 4.24. Выходящий документ

 

 

Следует обратить внимание на следующие особенности этого примера.

□ Элементы верхнего уровня в обязательном порядке должны иметь ненулевое пространство имен. Поэтому мы включили элемент input и все его дочерние узлы в пространство имен urn:user. В листинге 4.23 эти элементы выделены полужирным шрифтом.

□ В шаблонах, которые обрабатывают элементы включенного документа, должны указываться паттерны, соответствующие расширенным именам этих элементов, то есть не input, a user:input.

□ Чтобы не выводить объявления пространств имен в выходящем документе, мы включили префикс user в атрибут exclude-result-prefixes элемента xsl:stylesheet.

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

Пример

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

Листинг 4.25. Пользовательские данные в элементе верхнего уровня

 version="1.0"

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

 

 

  

  

 

 

 

 

   select="document('')/

   xsl:stylesheet/xsl:template[@name='input']/input"/>

 

 

 

 

 

 

 

 

 

  

 

 

Хитрость заключается в том, что мы обрабатываем содержимое именованного шаблона, которое вполне может принадлежать нулевому пространству имен. Единственное, что следует иметь в виду — это то, что этот шаблон не должен конфликтовать с другими шаблонами.

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

 

Литеральные элементы результата

 

Как мы уже видели из множества примеров, преобразования состоят не только из элементов языка XSLT. Например, в шаблоне

 <В/>

элемент B не принадлежит пространству имен XSLT и, следовательно, не считается XSLT-элементом. Такие элементы называются литеральными элементами результата (англ. literal result elements).

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

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

Пример

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

Теперь обратимся к случаю, когда один литеральный элемент будет включать другой:

 

 

 

При выполнении этого шаблона процессор создаст элемент A и включит в него обработанное содержимое — то есть элемент B. Результатом этого шаблона будет XML-фрагмент:

<А>

 <В/>

Теперь попробуем включить в содержимое элемента инструкцию XSLT:

 <А>

 

 

При выполнении этого шаблона процессор создаст результирующий элемент а и включит в него результат выполнения его содержимого, то есть элемента xsl:value-of. Этот элемент создаст текстовый узел ей строковым значением текущего узла контекста преобразования. Например, если бы мы обрабатывали этим шаблоном элемент а вида

Visit our site!

результатом выполнения был бы следующий элемент:

Visit out site!

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

 

  xmlns:xhtml="http://www.w3.org/1999/xhtml">

 

 

будет элемент вида:

 xmlns:xhtml="http://www.w3.org/1999/xhtml">

 Visit out site!

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

□ Процессор не будет копировать атрибуты, принадлежащие пространству имен XSLT.

□ Процессор не будет создавать узел пространства имен, соответствующий URI http://www.w3.org/1999/XSL/Transform, то есть URI пространства имен XSLT.

□ Процессор не будет создавать узлы пространств имен, префиксы которых исключаются атрибутами exclude-result-prefixes самого литерального элемента или элемента xsl:stylesheet.

Пример

Листинг 4.26

 version="1.0"

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

 xmlns:xhtml="http://www.w3.org/1999/XHTML">

 

 

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

   xslt:exclude-result-prefixes="xhtml">

  

 

 

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

□ В нем объявлено пространство имен с префиксом xhtml.

□ Литеральный элемент p содержит объявление пространства имен с префиксом xslt и URI http://www.w3.org/1999/XSL/Transform.

□ Литеральный элемент p содержит атрибут, xslt:exclude-result-prefixes, принадлежащий пространству имен XSLT.

Как ни странно, ни одно из этих объявлений не проникнет в выходящий документ, который будет иметь вид

4

Попробуем объяснить такой результат. Атрибут xslt:exclude-result-prefixes не был включен в результирующий элемент p, поскольку принадлежал пространству имен XSLT (отметим еще раз, что принадлежность эта определяется не префиксом, а значением URI). Далее, объявление пространства имен

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

которое содержалось в литеральном элементе p, не вошло в результат, потому что URI этого объявления совпадало с URI пространства имен XSLT. И, наконец, объявление пространства имен xhtml было исключено атрибутом exclude-result-prefixes.

 

Атрибуты языка XSLT в литеральных элементах

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

Таблица 4.3 . XSLT-атрибуты литеральных элементов

Атрибут Назначение
xsl:version Указывает версию языка в случае использования упрощенного синтаксиса записи преобразований
xsl:exclude-result-prefixes Перечисляет префиксы пространств имен, которые должны быть исключены в данном элементе
xsl:extension-element-prefixes Перечисляет префиксы пространств имен, которые используются в элементах расширения
xsl:use-attribute-sets Перечисляет названия именованных наборов атрибутов, которые следует включить в данный элемент на выходе

 

Шаблоны значений атрибутов

Во многих элементах XSLT в качестве значений атрибутов могут быть указаны специальные шаблоны, называемые шаблонами значений атрибутов (attribute value templates). Замечательное свойство этих шаблонов заключается в том, что вместо простых строковых значений в атрибутах можно использовать результаты вычисления выражений. Выражения в шаблонах значений атрибутов должны быть заключены в фигурные скобки ("{}"). Если процессор встретит внутри значения атрибута выражение в таких скобках, он должен будет вычислить это выражение и заменить его в атрибуте вместе с фигурными скобками на результат вычисления в строковом виде.

Пример

Довольно часто в практике программирования на XSLT встречается потребность создавать элементы с именами, которые заранее не известны, но могут быть вычислены в ходе выполнения преобразования. Представим себе документ

This text should be marked bold.

в котором атрибут type элемента mark-up указывает на тип элемента разметки, который должен быть использован для данного текстового фрагмента. Для того чтобы получить элемент вида

This text should be marked bold.

можно использовать следующий шаблон:

 

 

 

Таким образом, в качестве имени нового элемента, содержащего текст элемента mark-up, будет использовано значение атрибута type.

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

Пример

Предположим, что мы хотим вывести ссылки на графические изображения в виде иконок. Мы задаем список файлов в виде XML-документа:

 

 

 

Файлы хранятся в каталоге, указанном в атрибуте dir элемента images, а иконки имеют те же имена файлов, что и большие изображения, но с префиксом "th_". Для получения ссылок на изображения мы можем воспользоваться следующим преобразованием:

 <а href="{../@dir}/{@filename}">

 

 

Результат будет получен в виде:

<а href="/images/rose.jpg">

Для того чтобы использовать в значении атрибута левые и правые фигурные скобки в качестве простых символов, нужно удваивать их количество, то есть указывать "{{" вместо каждой левой и "}}" вместо каждой правой фигурной скобки соответственно.

Пример

Элемент, определенный как

 value="{{{{{{Enter your login here}}}}}}"/>

будет преобразован в выходящем документе к виду

Фигурные скобки нельзя использовать рекурсивно для вычисления внутри выражений. К примеру, в качестве значения атрибута name, определенного как

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

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

Пример

Элемент, определенный как

будет преобразован к виду

Шаблоны значений могут быть использованы далеко не везде. К примеру, не могут содержать шаблонов следующие типы атрибутов.

□ Атрибуты, значениями которых являются выражения.

□ Атрибуты, значениями которых являются паттерны.

□ Атрибуты элементов верхнего уровня.

□ Атрибуты пространств имен (xmlns).

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

атрибут src содержит ни что иное, как два шаблона значений.

Что же касается атрибутов элементов XSLT, то как очевидно из табл. 4.4, лишь малая их часть может содержать шаблоны значений.

Таблица 4.4 . Атрибуты элементов XSLT, которые могут содержать шаблоны значений

Элемент Атрибуты Описание
xsl:element name Имя создаваемого элемента
namespace Пространство имен создаваемого элемента
xsl:attribute name Имя создаваемого атрибута
namespace Пространство имен создаваемого атрибута
xsl:processing-instruction name Имя целевого приложения инструкции по обработке
xsl:number format Формат номера
lang Языковой контекст номера
letter-value Традиционная или алфавитная буквенная нумерация
grouping-separator Символ-разделитель групп цифр номера
grouping-size Размер группы цифр номера
xsl:sort lang Языковой контекст сортировки
data-type Тип данных сортировки
order Порядок сортировки
case-order Старшинство прописных и строчных символов при сортировке

Таким образом, перечень параметров, которые могут изменяться динамически (иными словами — вычисляться непосредственно во время выполнения шаблона) не так велик. В частности, стандартными способами в XSLT невозможно выполнить следующее.

□ Вызвать именованный шаблон динамически: атрибут name элемента xsl:call-template должен быть задан заранее и не может содержать шаблон значения.

□ Динамически изменить режим применения шаблонов (атрибут mode элемента xsl:apply-templates).

□ Вычислить элементами xsl:copy-of и xsl:value-of выражение заранее неизвестного вида.

□ Давать переменным и параметрам имена, вычисляемые во время выполнения преобразования.

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