Эта глава полностью посвящена созданию шаблонов и работе с ними как с основой таблиц стилей XSLT. Каждый шаблон образует правило, которое процессор XSLT пытается применить к исходному документу.
В главе 2 мы кратко рассмотрели таблицы стилей и основные шаблоны, при помощи которых наши примеры таблиц стилей в действительности могли что-то сделать. В этой главе мы собираемся подробно изучить шаблоны, а в следующей мы увидим, какие виды выражений можно применять для создания образцов выбора в шаблонах, позволяющих находить требуемые узлы. Образцы выбора (match pattern) являются подмножеством всего языка XPath и достаточно сложны для того, чтобы посвятить им отдельную главу.
В этой главе мы сначала рассмотрим, как работают основные шаблоны, и затем перейдем к таким темам, как правила шаблонов по умолчанию, выбор шаблона для работы, обработка атрибутов, создание мелких и глубоких копий элементов, завершение обработки шаблона и многое другое.
Создание шаблона
В главе 2 для выбора узлов в planets.xml и преобразования этого документа в HTML я создал основной шаблон. Шаблоны в таблицах стилей создаются при помощи элементов
xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">
.
.
.
.
.
.
Когда процессор XSLT находит узел, удовлетворяющий образцу вашего шаблона, этот узел становится контекстным узлом шаблона, то есть все операции производятся над этим узлом. На текущий узел можно ссылаться при помощи выражения XPath «.». Другие выражения XPath мы рассмотрим в этой главе и в главе 7.
УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0
В XSLT 1.0 возникали трудности при выборе элементов или атрибутов с нулевыми значениями, что должно быть исправлено в XSLT 2.0.
Внутри шаблона создайте разметку HTML, начинающую требуемую таблицу, — такая прямая вставка разметки называется элементом буквального (literal) результата. Когда процессор встречает литерал, он копирует его в результирующее дерево:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
.
.
.
Однако это правило обрабатывает только элемент
Обработка дочерних узлов
Элемент
Один важный момент часто вызывает затруднения: элемент
В следующем примере мы поместим элемент
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
.
.
.
В новом шаблоне, обрабатывающем элементы
.
.
.
Это можно сделать при помощи элемента
Доступ к значениям узлов
Элемент
Листинг 3.1. Полная версия planets.xsl
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
<Н1>
The Planets Table
Н1>
Name | Mass | Radius | Day |
Создание образцов выбора
Как вы могли ожидать, исходя из того, что "child::MASS" можно представить в виде "MASS" и применения шаблонов, таких как "/", "/PLANETS" и т.д., вплотную познакомиться с созданием образцов выбора не так-то просто — этому посвящена вся глава 4.
Образцы выбора (match pattern) являются подмножеством полного языка XPath, их можно использовать в элементах
• "/" выбирает корневой узел;
• "*" выбирает элементы узлов (но не всех узлов, как зачастую ошибочно полагают);
• "PLANET" выбирает элементы
• "PLANET/MASS" выбирает все элементы
• "//PLANET" выбирает все элементы
• "." выбирает текущий узел (технически это не образец выбора, а выражение XPath, как мы увидим в главе 7).
Образцы можно также использовать в атрибуте select элементов
Теперь пора воспользоваться атрибутом select элемента
Выбор применяемого шаблона
Вплоть до нынешнего момента я использовал только версию элемента
Name | Mass | Radius | Day |
Простое использование только
Например, до сих пор мы только получали значение каждого элемента
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Н1>
Name | Mass | Radius | Day |
Здесь только извлекается «сырое» строковое значение каждого узла, которое помещается в HTML-таблицу. Однако может потребоваться дополнительная обработка каждого элемента — например, получить также значения атрибутов UNITS каждого элемента и отобразить их:
.
.
.
Для этого нельзя просто применить элемент
Чтобы быть уверенным в том, что эти новые шаблоны применяются в правильном порядке, соответствующем заголовкам HTML-таблицы, я явно перечислю все новые шаблоны, выбирая их один за другим при помощи атрибута select:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
ИСПОЛЬЗОВАНИЕ ИМЕНОВАННЫХ ШАБЛОНОВ
К шаблонам можно также обращаться по имени. Этот вопрос будет рассмотрен в главе 9.
Теперь к каждому элементу
Листинг 3.2. Версия planets.xsl с выбором
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Этот код только воспроизводит то, что мы уже делали ранее в предыдущей версии planets.xsl, использующей
Чтение значений атрибутов
Чтобы получить доступ к значению атрибута при помощи XPath, нужно добавить к имени атрибута префикс @, например: "@src", "@height", "@width" и т.д.
Для выбора любого атрибута можно применить выражение "@*". Чтобы сослаться на атрибут UNITS в каждом элементе
Листинг 3.3. Чтение значений атрибутов
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Результаты работы показаны на рис. 3.1, Как видите, теперь мы извлекли строковое значение атрибута UNITS и отобразили его.
Рис. 3.1. Вывод значений атрибутов, первый вариант
Рисунок 3.1 не совсем точен: обратите внимание на то, что между значением и соответствующей единицей измерения в таблице нет пробела. Процессор XSLT просто поместил в результирующее дерево текст без каких-либо разделителей между текстовыми узлами. Хотя это в точности соответствует требованиям рекомендации XSLT, нам бы хотелось, чтобы элементы таблицы выглядели как «1516 miles», а не «1516miles». Как нам добавить этот дополнительный пробел?
Элемент <xsl:text>
Работа с пробелами всегда обсуждается при рассмотрении XSLT, и в данной главе я уделю этой теме некоторое время. Вставить единственный пробел несложно при помощи элемента
• disable-output-escaping. Устанавливается в yes для того, чтобы такие символы, как < и >, выводились буквально, а не как < и >. По умолчанию установлен в no.
Этот элемент может содержать только текстовый узел.
Текстовые узлы создаются при помощи элемента
Листинг 3.4. Вставка пробелов в таблицу стилей
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Новый результат виден на рис. 3.2, на котором между числовыми значениями и их единицами измерения вставлены пробелы.
Рис. 3.2. Вывод значений атрибутов, второй вариант
Как видите, элемент
УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0
Один из вопросов, которые призван решить XSLT 2.0, это упрощение импорта подобного текста без разбора из других файлов.
Отключение вывода эскейп-последовательностей
При помощи
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Planets
<PLANET/>
Результат следующий:
Planets
Разумеется, не обязательно выводить
Можно попробовать поместить этот элемент в раздел , как мы увидим в главе 6, и попытаться трактовать его как простые символьные данные, но процессоры XSLT все равно заменят < на <, а > на >.
Правильный способ добавить в вывод элемент — использовать атрибут doctype-public элемента
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
А вот результат:
The Planets Table
The Planets Table
.
.
.
Далее в книге будут приведены другие примеры использования
Написание значений атрибутов
В XSLT есть несколько способов написать значения атрибутов в выходные документы, и наиболее мощный — это создать атрибуты с нуля при помощи элемента
Однако можно также во многих случаях использовать шаблоны значений атрибутов, с которыми мы познакомимся в этой главе.
Предположим, например, что нам требуется преобразовать текст в таких элементах, как
MASS=".0553 (Earth = 1)" NAME="Mercury"/>
MASS=".815 (Earth = 1)" NAME="Venus"/>
MASS="1 (Earth = 1)" NAME="Earth"/>
Чтобы создать преобразование, нельзя просто использовать следующее выражение, в котором я взял значения элементов
MASS="
DAY="
/>
Это не будет работать, поскольку нельзя использовать < внутри значений атрибутов, как я сделал в предыдущем примере. В XSLT для этого есть несколько способов. Один из них — использовать шаблоны значений атрибутов.
Шаблоны значений атрибутов
Шаблон имени значения атрибута не имеет ничего общего с теми шаблонами, с которыми мы до сих пор работали, — то есть с шаблонами для создания правил таблицы стилей. Напротив, применение шаблона значения атрибута (attribute value template) означает лишь, что значение атрибута может быть установлено во время выполнения.
В этом случае атрибут можно установить в значение выражения XPath (более подробно эта тема изучается в главе 4), если заключить выражение в фигурные скобки. Например, чтобы установить атрибут NAME в строковое значение элемента
В листинге 3.5 приведен правильный код XSLT, в котором значения элементов
Листинг 3.5. Работа с шаблонами значений атрибутов
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот как это делается; посмотрите теперь на результирующий документ, в котором значения в различных элементах были преобразованы в атрибуты:
Предположим теперь, что нам также нужно включить все единицы измерения. Каждый элемент
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
MASS="{MASS} {MASS/@UNITS}"
RADIUS="{RADIUS} {RADIUS/@UNITS}"
DAY="{DAY} {DAY/@UNITS}"/>
И вот результат, включающий единицы измерения:
MASS=".0553 (Earth = 1)" NAME="Mercury"/>
MASS=".815 (Earth = 1)" NAME="Venus"/>
MASS="1 (Earth = 1)" NAME="Earth"/>
Заметьте, что в шаблонах значений атрибутов нельзя использовать вложенные фигурные скобки, и в выражении, использующем фигурные скобки, — таком как function printHello {cout << 'Hello';} — фигурные скобки необходимо удваивать, для того чтобы процессор XSLT их игнорировал: function printHello {{cout<<'Hello';}}.
Шаблоны значений атрибутов всегда работают с контекстным узлом. Тем не менее, нельзя использовать шаблоны значений атрибутов в произвольном месте таблицы стилей, что зачастую и вызывает затруднения у XSLT-разработчиков. Шаблоны значений атрибутов можно использовать только в следующих местах:
• элементы буквального результата;
• элементы расширения (см. главу 5);
•
•
•
•
•
В главе 6 эта тема рассмотрена более подробно: мы узнаем, как создавать атрибуты (и новые элементы) с нуля. Дополнительная информация об использовании выражений XPath в шаблонах значений атрибутов приведена в главе 7.
Обработка символов-разделителей
Поначалу символы-разделители (whitespace) доставляют авторам XSLT много хлопот. В главе 2 объяснялось, что «чистые» узлы-разделители — это текстовые узлы, содержащие только символы-разделители (пробелы, возвраты каретки, переводы строки и символы табуляции). Эти узлы по умолчанию копируются из исходного документа.
Заметьте, что в таблице стилей также могут быть узлы-разделители:
В нашем случае пробелы используются для выравнивания элементов таблицы стилей, а возвраты каретки — для разрежения кода. Чистые узлы-разделители, такие, как этот, не копируются из таблицы стилей в выходной документ. Заметьте, однако, что разделитель из следующего элемента
The Planets Table
.
.
.
Если вы хотите убрать этот разделитель и сохранить выравнивание, можно использовать пустые элементы
.
.
.
Чистые узлы-разделители не копируются из таблицы стилей в выходной документ, если только они не находятся внутри элемента
С другой стороны, по умолчанию XSLT сохраняет текстовые узлы разделители в исходном документе и копирует их в результирующий документ. Возьмите уже рассмотренную нами копирующую таблицу стилей, которая копирует все элементы из исходного документа в результирующий:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
и примените ее к planets.xml; все символы-разделители будут также скопированы в результирующий документ:
.
.
.
Тем не менее, иногда требуется удалить разделители, используемые при форматировании входных документов — это можно сделать при помощи элемента
Элементы <xsl:strip-space> и <xsl:preserve-space>
Элемент
• elements (обязательный). Задает элементы, из которых нужно убрать символы-разделители. Представляет, собой список разделённых символами-разделителями NameTest (именами или обобщёнными именами с символами подстановок).
Элемент не включает содержимого. Например, чтобы удалить все узлы-разделители из planets.xml, можно применить
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Вот результирующий документ, полученный после применения этой таблицы стилей к planets.xml. Обратите внимание на то, что убраны все символы-разделители, в том числе все символы новой строки:
Заметьте, что таким образом удаляются только чистые узлы-разделители. Например, текст элемента
В некоторых ситуациях может потребоваться не удалять все узлы-разделители из всего документа; задать элементы, в которых следует сохранить узлы-разделители, можно при помощи элемента
• elements (обязательный). Задает элементы, в которых нужно сохранить символы-разделители. Представляет собой список разделенных символами-разделителями NameTest (именами или обобщенными именами с символами подстановок).
Фактически элемент
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Обсужденные средства удаления и сохранения разделителей могут показаться слишком сложными для форматирования выходных документов выравнивающими пробелами, но, к счастью, существует простой способ: атрибут indent элемента
Автоматическое выравнивание
Элемент
Способ работы процессора XSLT с переменной выравнивания не регламентируется W3C и зависит от процессора, поэтому для получения требуемого результата нужно экспериментировать. Пусть, например, у нас есть версия planets.xml без какого-либо выравнивания:
При помощи элемента
Листинг 3.6. Таблица стилей, задающая выравнивание
The Planets Table
The Planets Table
Name | Mass | Radius | Day>/TD>
|
Результат применения таблицы с использованием процессора Saxon (в котором особенно хорошо реализовано выравнивание) с требуемым выравниванием:
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Mercury | .0553 | 1516 | 58.65 |
Venus | .815 | 3716 | 116.75 |
Earth | 1 | 2107 | 1 |
Как видите, в XSLT обработке символов-разделителей приходится уделять достаточное внимание, но процедура упрощается, если вы знаете, что происходит.
ВЫРАВНИВАНИЕ ДОКУМЕНТОВ В ЭТОЙ КНИГЕ
Способ выравнивания документов зависит от конкретного процессора XSLT. В этой книге документы выровнены для удобочитаемости, даже если в действительности документы не были выровнены процессором XSLT.
Правила по умолчанию в шаблоне
Взгляните на следующую таблицу стилей XSLT — в ней заданы правила для выбора корневого узла, узлов
xmlns:xsl="http.//www.w3.org/1999/XSL/Transform">
Обратите внимание на правило для элемента
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
В этом случае я воспользовался преимуществом правил по умолчанию для шаблона. Ниже приведены правила для каждого вида узлов, которые будут применены, если не задать правило для узла явно:
• Корневой узел. По умолчанию вызывается
• Узлы элементов. По умолчанию вызывается
• Узлы атрибутов. Копирует в результирующий документ значение атрибута, однако копирует его как текст, но не как атрибут;
• Текстовые узлы. Копирует в результирующий документ текст;
• Узлы комментариев. Нет обработки XSLT, ничего не копируется;
• Узлы инструкций обработки. Нет обработки XSLT, ничего не копируется;
• Узлы пространств имен. Нет обработки XSLT, ничего не копируется.
Наиболее важное правило по умолчанию применяется к элементам и может быть выражено следующим образом:
Это правило приведено здесь только для гарантии того, что каждый элемент, от корня и ниже, будет обрабатываться при помощи
Правило по умолчанию для текстовых узлов можно выразить следующим образом: функция XSLT text выбирает текст узла, так что текст текстового узла добавляется в выходной документ:
Правило по умолчанию того же вида применяется к атрибутам, которые добавляются в выходной документ при помощи следующего правила, где выражение "@*" выбирает любой атрибут:
По умолчанию инструкции обработки не вставляются в выходной документ, поэтому их правило по умолчанию можно выразить просто при помощи следующей функции-инструкции обработки XSLT, которая выбирает инструкции обработки (как мы увидим в главе 8):
То же верно для комментариев — их правило по умолчанию может быть выражено при помощи функции XSLT comment, которая также будет рассмотрена в главе 8:
Подведем итоги рассмотрения правил по умолчанию: если вообще не задать никаких правил, все разбираемые символьные данные входного документа будут вставлены в выходной документ. Вот как выглядит таблица стилей, в которой не задано никаких явных правил:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
А вот результат применения этой таблицы стилей к planets.xml. Заметьте, что правило по умолчанию для атрибутов не применялось, потому что они не являются дочерними по отношению к другим узлам:
Mercury
.0553
58.65
1516
.983
43.4
Venus
.815
116.75
3716
.943
66.8
Earth
1
1
2107
1
128.4
ПРАВИЛА ПО УМОЛЧАНИЮ И INTERNET EXPLORER
Одна из проблем при работе с XSLT в Internet Explorer 5.5 или младше состоит в том, что браузер не предоставляет никаких правил по умолчанию. Необходимо задавать все правила самостоятельно, если только не установлен процессор MSXML3 в режиме замены (подробнее см. главу 2) или вы не обновили браузер до Internet Explorer 6.0.
Кроме того, узлы-разделители исходного документа сохраняются, поэтому можно считать, что следующее правило также является правилом по умолчанию:
Удаление содержимого
Если ваше правило для узла не работает, то есть оно пусто, содержимое выбранного узла не будет скопировано в выходной документ. Таким способом при создании выходного документа можно выборочно удалять содержимое из исходного документа.
Предположим, нам нужно удалить из planets.xml все данные о планетах, за исключением их названий и данных о массе. Следующая таблица стилей выполняет данную задачу.
Листинг 3.7. Удаление содержимого
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
А вот результирующий документ (отметьте, что я сохранил только элементы
Таким способом можно фильтровать XML-документы, создавая новые XML-документы только с требуемыми данными.
Разрешение конфликтов шаблонов
Еще одним важным аспектом работы с шаблонами является разрешение конфликтов. Если двум шаблонам удовлетворяют один и тот же узел или набор узлов, для определения применяемого шаблона XSLT учитывает их приоритет.
У каждого шаблона есть приоритет по умолчанию, основанный на значении атрибута select. Как правило, чем более сужающим является правило выбора или выражение (например, "PLANET" и "*"), тем выше его приоритет. В главе 4 мы рассмотрим, как процессор определяет приоритеты и как он работает с шаблонами, имеющими одинаковый приоритет.
Приоритет шаблона можно установить при помощи атрибута priority. В приведенном ниже листинге 3.8 правило, созданное элементом
Листинг 3.8. Установка приоритета шаблона
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
(Very heavy)
Процессор XSLT выбирает шаблон с наивысшим приоритетом, который добавляет текст "(Very heavy)" после каждого значения массы. В следующем примере использован шаблон с наивысшим приоритетом:
The Planets Table
The Planets Table
Н1>
Name | Mass | Radius | Day |
Mercury | .0553(Very heavy) | 1516 miles | 58.65 days |
Venus | .815(Very heavy) | 3716 miles | 116.75 days |
Earth | 1(Very heavy) | 2107 miles | 1 days |
УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0
Вопрос приоритета шаблонов должен быть учтен в XSLT 2.0. В частности, W3C рассматривает возможность добавления нового элемента с предварительным названием <xsl:next-match/>, который позволит выбирать для шаблона второй лучше всего подходящий элемент.
О приоритетах полезно знать еще одно: если двум шаблонам удовлетворяет один и тот же узел, и этим шаблонам не были присвоены приоритеты, процессор XSLT выберет шаблон с более узким правилом выбора. Например, условию "PLANET" будет отдано предпочтение перед обобщенным условием "*".
Элемент <xsl:copy>
Элемент
• use-attribute-sets. Задает названия наборов атрибутов, которые нужно применить к создаваемому элементу. Принимает значение списка QName, разделенных символами-разделителями. Этот атрибут можно использовать только в том случае, когда контекстный узел является элементом. Дополнительная информация о наборах атрибутов приведена в главе 6.
Этот элемент может содержать тело шаблона, которое используется только когда копируется корневой узел или элемент. Заметьте, что при применении к корневому узлу элемент
Приведенная в листинге 3.9 таблица стилей впервые появилась в главе 2; все, что она делает, — копирует все элементы из исходного документа в результирующий.
Листинг 3.9. Таблица стилей, копирующая элементы
version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Однако
Копирование атрибутов несколько сложнее, потому что нужно найти какой-либо способ применить
Листинг 3.10. Копирование атрибутов
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
А вот результат — заметьте, что на этот раз атрибуты не затронуты:
Но есть более простой путь проверить, что копируются все дочерние узлы, атрибуты и другие потомки узлов: вместо элемента
ГЛУБОКОЕ КОПИРОВАНИЕ
Пример использования <xsl:copy> для осуществления глубокого копирования документа будет приведен в главе 4, в которой описывается функция узла и объясняется, как рекурсивно вызывать один и тот же шаблон.
Элемент <xsl:copy-of>
Элемент
• select (обязательный). Узел или набор копируемых узлов. Этот элемент пуст и не имеет содержимого.
Ниже приведен пример работы этого элемента; в этом случае я заменил в листинге 3.10 элемент
Листинг 3.11. Применение <copy-of>
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Этот код работает так же, как и предыдущий пример, копируя все элементы и атрибуты. С другой стороны, можно вообще обойтись без каких-либо изменений в листинге 3.10, — я могу просто использовать
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
При помощи
По этой причине я могу заменить элемент
Элемент <xsl:message>
При помощи элемента
• terminate (необязательный). Значение «yes» прекращает обработку. По умолчанию установлено «no».
Куда на самом деле будет отправлено сообщение, зависит от процессора XSLT. Для процессоров, основанных на Java, сообщение обычно отправляется в выходной поток ошибок Java, которому соответствует экран компьютера, если процессор XSLT был вызван из командной строки. Другие процессоры XSLT могут выводить сообщения во всплывающие окна или в web-страницы, отправляемые в браузеры.
В приведенном ниже листинге 3.12 я прекращаю обработку XSLT, когда процессор XSLT пытается преобразовать элемент
Листинг 3.12. Применение <xsl:message>
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
The Planets Table
The Planets Table
Name | Mass | Radius | Day |
Sorry. DAY information is classified.
Вот результаты применения этой таблицы стилей в Xalan:
C:\planets>java org.apache.xalan xslt.Process -IN planets.xml -XSL message.xsl -OUT planets.html
file:///C:/XSL/messages/message.xsl: Line 49; Column 38;
Sorry. DAY information is classified.
XSLT Error (javax.xml.transform.TransformerException):
Stylesheet directed termination
При помощи элемента
Далее в книге будут рассмотрены и другие посвященные шаблонам темы — такие, как вызов именованных шаблонов и использование параметров. В следующей главе мы перейдем к большой и важной теме — созданию образцов выбора, при помощи которых в XSLT можно выбирать, какие узел или узлы вам нужны для работы. Мы уже затрагивали тему работы с образцами выбора, теперь же пора перейти к их систематическому рассмотрению.