• должны быть добавлены функции форматирования даты для поддержки схемы XML;
• должна быть включена функция для преобразования относительных идентификаторов URI в абсолютные;
• должны быть включены функции, упрощающие группировку. В XSLT 2.0 должно быть возможно группировать узлы на основе их строковых значений.
В XPath 2.0 в отношении функций планируются следующие изменения:
• должен быть расширен набор агрегирующих функций — например, включены функции минимума и максимума;
• для наборов узлов должны быть реализованы функции пересечения и разности;
• должна быть включена поддержка дополнительных строковых функций, таких как функции для замены строк, заполнения символами и преобразований регистров в строках;
• должны быть включены агрегирующие функции для работы с коллекциями такими как наборы узлов;
• должны быть определены функции для работы со стандартными типами схемы XML.
На этом мы заканчиваем рассмотрение функций XSLT и XPath, применяемых при преобразованиях. В главе 9 мы изучим переменные, параметры и другие специальные темы.
Глава 9Именованные шаблоны, параметры и переменные
В этой главе рассматривается ряд специальных возможностей XSLT, а именно шаблоны, параметры таблиц стилей и переменные. Все эти темы связаны друг с другом: параметры используются в именованных шаблонах, а переменные и параметры — это практически одно и то же, они различаются только способом создания.
Если задать шаблону имя, его можно вызывать по этому имени при помощи элемента
. Шаблон применяется при вызове, поэтому вместо того чтобы полагаться на обработку таблицы стилей, принятую в процессоре XSLT по умолчанию, вы можете явно задать, когда и какой шаблон нужно применять. Например, несколько шаблонов могут выбирать один и тот же набор узлов, и вам нужно выбрать из них один или несколько шаблонов, которые будут применены. Именованные шаблоны похожи на режимы, но дают вам больше возможностей управления.При вызове шаблона можно настроить его работу при помощи параметров. Например, вам может понадобиться, чтобы текст в создаваемых шаблоном текстовых узлах был на определенном языке — таком как английский, немецкий или французский — и вы можете создать новый параметр с именем
language
(язык). При вызове именованного шаблона, заданного для обработки этого параметра, вы можете установить язык в «en
», «de
» или «fr
» и затем вызвать шаблон при помощи элемента
. В самом именованном шаблоне используемый им параметр language
объявляется при помощи элемента
. После объявления параметра к его значению можно свободно обращаться как $language
и использовать его в выражениях XPath. В этой главе мы рассмотрим многочисленные примеры работы с параметрами.Переменные во многом похожи на параметры, с тем лишь отличием, что они по-другому создаются. Параметры, как правило, используются в именованных шаблонах, в то время как переменные применяются более широко, в выражениях XPath любого вида. Как и в языках программирования, в переменных XSLT можно хранить значения и обращаться к ним позже. Но есть одна важная особенность: за исключением особых обстоятельств, вы не можете изменять значение, хранимое в переменной. (В связи с этим некоторые авторы, пишущие об XSLT, считают неправильным называть их переменными.)
Переменные удобны для хранения значений, создание которых занимает длительное время, но в таблице стилей они часто используются. Вместо того, чтобы каждый раз заново создавать эти значения, сохраните их в переменной и ссылайтесь на ее значение. Как и в случае с параметрами, для получения значения переменной добавьте префикс «$». Например, для переменной с именем
sandwich
получить ее значение можно при помощи $sandwich
. Как и в параметрах, в переменных можно хранить данные всех четырех типов данных XPath. Переменные также имеет смысл применять для хранения значений, которые позже в шаблоне будут изменены. Например, «.» обычно ссылается на контекстный узел шаблона, но внутри элемента
«.» ссылается на текущий обрабатываемый в элементе узел, а не на контекстный узел всего шаблона. Для того чтобы обратиться к контекстному узлу, перед входом в цикл
сохраните его в переменной contextnode
и затем в теле цикла используйте это значение как $contextnode
.Кроме четырех типов данных XPath мы также будем использовать тип данных, поддерживаемый в XSLT 1.0, но не в XSLT 1.1 — фрагменты результирующего дерева, которые создаются элементами
или
. Фрагменты результирующего дерева могут быть удобны в определенных случаях, как вы увидите далее в этой главе.Наконец, в этой главе мы также рассмотрим элемент
. Впервые он нам встретился в главе 4, теперь мы изучим его более подробно.Для введения вполне достаточно; давайте перейдем к работе, и начнем мы с переменных.
Элемент : создание переменных
Для создания переменных в XSLT служит элемент
, обладающий следующими атрибутами:•
name
(обязательный). Имя переменной, устанавливается в QName;•
select
(необязательный). Выражение XPath, задающее значение переменной. Если опустить этот атрибут, значение переменной будет определяться содержимым
.Этот элемент может либо быть элементом верхнего уровня, либо применяться внутри тела шаблона. Элемент может сам содержать тело шаблона, но в таком случае нельзя использовать атрибут
select
.Для создания переменной присвойте ее имя атрибуту name элемента
, а значение переменной атрибуту select
, как в следующем примере, в котором я создаю переменную number_books
и сохраняю в ней значение 255:
.
.
.
Получить значение переменной можно, добавив к ее имени префикс $:
There are
books in my library
Заметьте, что если вы присваиваете переменной литерал — как, например, присваивание значения «turkey» (индейка) переменной
sandwich
(бутерброд), — литерал необходимо заключить в кавычки, причем они должны отличаться от кавычек, в которые заключены значения атрибутов:
В XSLT 1.0 нет необходимости в атрибуте select — данные можно заключить внутри самого элемента
:turkey
Формально, однако, при пропуске атрибута
select
в элементах
или
и задании этим элементам содержимого вы создаете фрагмент результирующего дерева, который больше не допускается в XSLT 1.1.Стоит отметить, что имя переменной может включать префикс, как, например,
star:PLANET
, который должен соответствовать активному пространству имен. Сравнения осуществляются не сравнением префиксов, а проверкой фактического URI префикса — поэтому star:PLANET
может быть тем же самым, что и nebula:PLANET
, если пространства имен star
и nebula
соответствуют одному и тому же URI.Область видимости переменной
Элемент
можно использовать как элемент верхнего уровня или внутри тела шаблона для создания переменных. Переменные, созданные в элементах
высокого уровня, обладают глобальной областью видимости, созданные в телах шаблона — локальной. Область видимости переменной определяет, в какой части таблицы стилей вы можете ее использовать.Областью видимости глобальной переменной является вся таблица стилей, подразумевая и импортированные или включенные таблицы стилей. Это означает, что переменная доступна в любом месте таблицы стилей, если только она не будет перекрыта локальной переменной с тем же именем. Можно даже обращаться к глобальной переменной до ее объявления. Однако нельзя создавать циклические ссылки (то есть если вы объявили
a
через b
, нельзя объявлять b
через а
).Область видимости локальной переменной ограничена следующими за ней братьями или потомками последующих братьев. В частности это значит, что если вы объявили переменную внутри таких элементов, как
,
или
, она не будет доступна вне этих элементов.Как правило, вы не можете изменять значение переменной, но вы можете перекрыть ее локальной переменной. То есть локальные переменные перекрывают глобальные в пределах области видимости локальных переменных. Пусть, например, я объявил переменную с именем
movie
(кинокартина):
.
.
.
Это элемент верхнего уровня, поэтому
movie
— глобальная переменная. Даже внутри шаблонов movie будет сохранять свое начальное значение, если не будет локальной переменной с таким же именем:
.
.
.
Однако если вы объявите локальную переменную movie, в шаблоне эта версия перекроет глобальную переменную:
.
.
.
В этом случае мы перекрыли глобальную переменную при помощи глобальной. Заметьте, однако, что нельзя снова объявить одну и ту же переменную в одном шаблоне с целью попытаться изменить ее значение:
.
.
.
За пределами шаблона локальная переменная невидима, и
movie
содержит глобальное значение:
.
.
.
Глобальные переменные тоже нельзя объявлять повторно:
Несмотря на все эти ограничения, вы можете менять значение переменной на каждом шаге цикла
, как мы увидим в следующем разделе.Работа с переменными
Давайте рассмотрим примеры применения переменных. В следующем примере (листинг 9.1) я присваиваю переменной
copyright
сообщение об авторских правах и затем с ее помощью добавляю атрибут copyright
во все элементы planets.xml
.Листинг 9.1. Применение переменной
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот результирующий документ, дополненный атрибутами copyright
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
.
.
.
Переменные зачастую удобны для хранения значении, зависимых от контекста, и мы сейчас рассмотрим еще один пример, о котором я упоминал в начале главы. В этом случае я преобразую
planets.xml
в новый документ, в котором для каждой планеты будет один элемент. Каждый из этих новых элементов будет содержать два элемента
, содержащих планеты-братья текущей планеты — например, братьями Земли будут Венера и Меркурий:
Venus
Earth
Mercury
Earth
Mercury
Venus
Для примера я поочередно выбираю каждый элемент
и прохожу в цикле
по всем планетам, создавая элементы
для всех планет, не являющихся контекстным узлом. Однако откуда мне известно внутри элемента
, какая из планет является контекстным узлом, выбранным шаблоном? Внутри элемента
«.» ссылается на текущий узел, с которым работает
, но не на контекстный узел шаблона. Проблему можно решить, если сохранить контекстный узел в переменной, которую я назвал contextnode
:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
.
.
.
Теперь для проверки в цикле
того, что текущий элемент не является контекстным узлом, я могу обратиться к контекстному узлу шаблона как $contextnode
(листинг 9.2).Листинг 9.2. Хранение в переменной информации, зависимой от контекста
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Теперь наша проблема решена.
Если у элемента
есть тело, он создает переменную, чье значение является фрагментом результирующего дерева. В следующем примере при помощи фрагмента результирующего дерева я задаю значение по умолчанию для атрибута COLOR
(цвет), если значение для него уже не задано. Значение по умолчанию я устанавливаю в «blue
» (голубой):
blue
Строковое значение фрагмента результирующего дерева (то есть либо значение атрибута
COLOR
, либо значение по умолчанию, «blue
») присваивается переменной COLOR
. Теперь в выражениях XPath можно обращаться к значению этой переменной, $COLOR
, а не к значению атрибута (@COLOR
, гарантированно получая при этом значение цвета, даже если у соответствующего элемента отсутствует атрибут COLOR
.Вот еще один пример фрагмента результирующего дерева. В этом случае я сохраняю элемент буквального результата в переменной
START_HTML
:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
My page
.
.
.
Теперь я могу использовать этот элемент буквального результата где угодно:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
My page
Welcome to my page
И вот результат:
My page
Welcome to my page
Однако поскольку теперь фрагменты результирующего дерева не допускаются в XSLT 1.1, этот пример работать не будет. Как же тогда сохранить весь элемент буквального результата одновременно с возможностью простого вызова? Вы можете создать именованный шаблон.
Элемент