Технология XSLT — страница 17 из 66

Определение шаблонного правила

Элемент xsl:template

Синтаксис этого элемента приведен ниже:

 match="пaттерн"

 name="имя"

 priority="число"

 mode="имя">

Элемент верхнего уровня

xsl:template
определяет в преобразовании шаблонное правило, или просто шаблон. Элемент
xsl:template
имеет всего четыре атрибута, смысл которых мы кратко опишем ниже.

Атрибут

match
задает паттерн — образец узлов дерева, для преобразования которых следует применять этот шаблон.

Пример

В этом правиле атрибут

match
говорит о том, что оно должно использоваться для обработки элементов
bold
— в данном случае они будут заменяться на элементы
b
. Шаблоны, в которых определен атрибут
match
, вызываются при помощи инструкции
xsl:apply-templates
.

Шаблон также может иметь имя, определяемое атрибутом

name
. Шаблон, в котором задано имя, называется именованным шаблоном. Именованные шаблоны могут вызываться вне зависимости от текущего контекста, и даже вести себя как функции — принимать на вход параметры и возвращать некоторые значения.

Пример

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

xsl:call-template
.

При определении шаблона нужно обязательно указать хотя бы один из атрибутов

match
или
name
, причем эти атрибуты могут присутствовать в
xsl:template
одновременно.

Атрибут

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

Атрибут

priority
используется для определения значения, которое называется приоритетом шаблонного правила. Это значение используется для разрешения конфликтов шаблонов в случае, когда один узел может быть обработан различными правилами.

Атрибуты шаблонного правила не влияют на выполнение его содержимого. Они используются элементами

xsl:apply-templates
и
xsl:call-template
при выборе шаблонов. Правила, которые были импортированы в преобразование, вызываются элементом
xsl:apply-imports
.

Вызов шаблонных правил

Рассмотрим следующий простой пример.

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

text

Попробуем написать пару шаблонов, которые будут изменять имена элементов

para
и
bold
на
p 
и
b
соответственно. Сначала напишем преобразование для
bold
:

В этом правиле создается элемент

b
, в который включается текстовое значение текущего узла (то есть, обрабатываемого элемента
bold
). Применив это преобразование к входящему документу, мы получим следующий результат:

text

Как говорят математики, что и требовалось. Попробуем проделать тот же трюк с элементом

para
и создадим преобразование, включающее оба правила.

Листинг 5.2. Преобразование с para и bold — версия 1

 version="1.0"

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



На этот раз вместо ожидаемого результата вида

text

мы получим

 text

Попробуем ответить на три вопроса: кто виноват, что делать и куда делся элемент

b
.

Для ответа на вопрос, куда делся элемент

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

□ Процессор начинает обработку с корневого узла дерева. Он выбирает шаблон, соответствующий этому узлу. В нашем преобразовании такого шаблона нет, значит, процессор применит к корню шаблонное правило, определенное по умолчанию (см. раздел "Встроенные шаблоны" данной главы).

□ По умолчанию шаблонное правило корневого узла обрабатывает все дочерние узлы. В нашем документе единственным дочерним узлом корня будет элемент

para
.

□ Для элемента

para
в нашем преобразовании задан шаблон, который и будет применен к этому элементу.

□ В соответствии с этим шаблоном, процессор создаст элемент

p
и включит в него текстовое значение выражения "
.
". Как мы знаем, выражение "
.
" является сокращенной формой выражения "
self::node()
", которое возвратит текущий узел. Таким образом, элемент
вычислит и возвратит строковое значение текущего узла, то есть узла
para
. Строковым значением элемента является конкатенация всех его текстовых потомков. Единственным текстовым потомком нашего para является текстовый узел со значением "
text
" и вот он-то и выводится между открывающим и закрывающим тегами созданного элемента
p
.

Таким образом, элемент

b
"потерялся" потому, что шаблон для
bold
просто не вызывался. Виноваты, естественно, мы сами, поскольку не включили его вызов. Осталось только разобраться, как можно вызвать шаблон для обработки элемента
bold
.

Ответ на этот вопрос предельно прост — для вызова неименованных шаблонных правил В XSLT используется элемент

xsl:apply-templates
.

Элемент xsl:apply-templates

Синтаксис этого элемента выглядит следующим образом:

 select="выражение"

 mode="режим">

Элемент

xsl:apply-templates
применяет шаблонные правила к узлам, которые возвращаются выражением, указанным в атрибуте
select
. Если атрибут
select
опущен, то
xsl:apply-templates
применяет шаблонные правила ко всем дочерним узлам текущего узла, то есть

равносильно

Атрибут

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

Прежде чем двигаться дальше, опишем более подробно, что означает "применить шаблон" (англ. apply — применить, template — шаблон). Применение шаблонов — это составная часть обработки документа, которая может быть описана следующим порядком действий.

□ На первом шаге процессор вычисляет выражение, указанное в атрибуте

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

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

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

Применительно к нашему примеру с

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

Листинг 5.3. Преобразование с para и bold — версия 2

 version="1.0"

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




Проследим за процессом выполнения этого преобразования.

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

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

□ Шаблон, соответствующий элементу

para
, создает элемент
p
, содержимым которого будет результат выполнения инструкции
xsl:apply-templates
, то есть результат применения шаблонов к дочерним узлам текущего узла — элемента
para
.

□ Единственным дочерним узлом элемента

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

Три шага этого преобразования продемонстрированы на рис. 5.1.

Рис. 5.1. Процесс преобразования

Здесь слева показан текущий список узлов, посередине — дерево документа с выделенным пунктиром текущим узлом, справа — генерируемое выходящее дерево.

Результатом этого преобразования будет документ:

text

Рассмотрим чуть более сложное преобразование документа:

text1

text2

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

□ Первым обрабатывается корневой узел. Процессор применяет шаблоны к дочерним узлам (вернее к одному дочернему узлу — элементу

para
).

□ Шаблон, обрабатывающий элемент para, создает в выходящем документе элемент p и применяет шаблоны к своим дочерним узлам — на этот раз их два,

bold
и
para
.

□ Шаблон, обрабатывающий элемент

bold
, создает в выходящем документе элемент
b
и текстовый узел со значением "
text1
".

□ Шаблон, обрабатывающий элемент

para
, создает в выходящем дереве узел
p
и применяет шаблоны к дочерним узлам.

□ Единственным дочерним узлом элемента

para
является элемент
bold
.

□ Шаблон, обрабатывающий этот элемент

bold
, создает в выходящем документе элемент
b
и текстовый узел со значением "
text2
".

Процесс преобразования показан на рис. 5.2.

Рис. 5.2. Процесс преобразования

Результатом этого преобразования будет документ:

text1

text2

Атрибут

select
элемента
xsl:apply-templates
позволяет выбирать, к каким именно узлам будет применяться этот шаблон. Значение
select
— это XPath-выражение, которое должно возвращать множество узлов. В случае, если атрибут
select
указан, шаблоны будут поочередно применяться к каждому из узлов выбранного множества.

Пример

Если при обработке элементов para мы хотим обрабатывать только дочерние элементы

bold
и никакие другие, шаблон обработки элементов
para
будет записан следующим образом:

Результатом обработки документа

text1

text2

будет теперь

text1

Элемент

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

то результат будет таким же, как и прежде:

text1

text2

Следует хорошо понимать разницу между атрибутом

select
элемента
xsl:apply-templates
и атрибутом
match
элемента
xsl:template
. Атрибут
match
содержит не XPath-выражение, а паттерн XSLT; в отличие от атрибута select в
xsl:apply-templates
он не выбирает никакого множества узлов, он используется только для того, чтобы проверить, может ли данный узел обрабатываться этим шаблоном или нет.

Атрибут

select
элемента
xsl:apply-templates
наоборот, содержит не паттерн, а выражение, единственным требованием к которому является то, что оно должно возвращать множество узлов. Например, некорректным будет определение вида

поскольку выражение

para+1
не может возвратить множество узлов.

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

id
или
key
), выражения с операциями над множествами (именно таким выражением — выражением объединения было выражение
bold|para
), пути выборки, фильтрующие выражения, в общем, любые выражения, которые только могут возвращать множества. Например, для того, чтобы обработать содержимое произвольного внешнего XML-документа, в атрибуте
select
элемента
xsl:apply-template
следует использовать функцию
document
.

Пример

Объявление вида

применит шаблоны ко всем элементам

para
документа
a.xml
.

Режимы