Шаблонизация на XSLT. Приемы и примеры
Вместо введения.
Прохаживаясь по страницам нашего
форума и отвечая на вопросы я пришел к выводу, что большинство спрашивающих
посетителей в основных чертах знают основы применения XSLT преобразования, но на
практике сталкиваются с рядом вопросов - а как же это использовать?
Про XSLT-преобразование написано много статей, и мне не хотелось бы
повторяться и пересказывать теорию XSLT. В данной статье речь пойдет о некоторых
приемах и хитростях XSLT-разработчика. На такое громкое название, как CookBook
(книга рецептов), статья не претендует, но парой хитростей я поделюсь. Статья
рассчитана на начинающих пользователей XSLT-шаблонизации, знающих хотя бы ее
основы.
Начнем с азов.
Получение выходного HTML-кода получается путем преобразования
XSLT-процессором входных XML-данных по XSL-шаблону. Соответственно, умение
организовать правильный выходной поток, т.е. наш HTML-код, состоит из умения
правильно организовать XML-данные и умения правильно писать шаблоны. (Хотелось
бы отметить, что использование XSLT-преобразования не ограничивается генерацией
только выходного HTML-кода).
Отсюда напрашивается вывод, что, прежде чем искать ошибку в шаблонах, надо
проверить сами XML-данные, т.е. их структуру и состав. Проверка организуется
двумя способами: либо выводом в лог-файл, либо выводом XML-данных вместо
результата XSLT-процессора.
Если мы используем второй способ (вывод в браузер), то, чтобы эти данные
опознал браузер как XML поток и представил в удобопонимаемом виде, необходимо
выдать заголовок Content-type: text/xml: <?
...
Header( "Content-type","text/xml");
print( $xml );
?>
Необходимо отметить, что когда пишется XSL-шаблон, то это уже не HTML, а XML,
и надо руководствоваться правилами валидности XML:
- Каждому открывающему тегу должен соответствовать закрывающий тег. Это в
основном касается парности таких тегов, как
<table> ,
<td> , <tr> .
- Если тег представлен без пары (одинарный), то он должен иметь закрывающий
слэш. Это в основном касается таких одинарных тегов, как
<br/> и <img/> .
Практически это означает, что нельзя перемешивать теги, должна быть четкая
иерархия вложенности. Например, такие конструкции как:
<b>bla-bla-bla <i> bla-bla-bla </b>
bla-bla-bla</i> валидны в HTML, но недопустимы в XML.
Tекст спецификации XML (версии 1.0 и 1.1) можно найти по адресу: http://www.compdoc.ru/internet/xml/spec/
и http://www.compdoc.ru/internet/xml/1_1_specifications/.
Использование включений
Вот уже готово Ваше преобразование, осталось только придать внешний вид
сгенерированной странице. Нет проблем, когда страница одна или две. Проблемы
появляются, когда их больше десятка и весь сайт надо сделать в едином стиле.
Действительно, не будешь же вносить повторяющиеся части HTML кода в каждый
шаблон. В PHP-шаблонизаторах это решалось путем включений: %include header.tpl
...
// текст шаблона
...
%include footer.tpl
В XSLT-преобразованиях есть аналогичный механизм >xsl:include
/< .
Имеется файл main.xsl, который содержит «генеральный» шаблон, единый для всех
страниц: <xsl:template match="root">
<HTML>
<BODY>
<div align="center"><b>TABLE OF PRICE </b><br/>
<table border="0" bgcolor="#000080" cellpadding="1" cellspacing="1">
....
<xsl:apply-templates select="item" />
....
</table></div>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
В данном шаблоне есть правило <xsl:apply-templates select="item"
/> , которое применяется ко всем элементам xml-документа. Данная
инструкция, может быть заменена на <xsl:call-template name="item"
/> .
В нашем преобразовании должна быть инструкция <xsl:include
href="main.xsl" /> и, соответственно, шаблонное правило, определенное
в «генеральном» шаблоне: <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="Windows-1251"/>
<xsl:include href="main1.xsl" />
<xsl:template match="/">
<xsl:apply-templates select="root" />
<!—Вызывает правило, определенное в генеральном шаблоне -->
</xsl:template >
<!—Правила, локальных шаблонов -->
<xsl:template match="item">
<tr bgcolor="#ffffff" align="center">
<td align="left">
<xsl:value-of select="description"/>
</td>
<td align="center">
<xsl:value-of select="price"/>
</td>
</tr>
</xsl:template >
</xsl:stylesheet>
Если мы используем в «генеральном» шаблоне инструкцию
<xsl:call-template name="item" /> то вместо
<xsl:template match="item"> используем именной шаблон:
<xsl:template name="item"> . В этом случае и технология
разработки шаблонов – иная.
В любом случае при использовании «генеральных» шаблонов надо придерживаться
строго определенного формата выходных xml-данных.
Например, у меня следующая структура: <root>
<menu>
<item name=""/>
<! -- пункты динамически сформированного меню -->
...
</menu>
<<action>>
<</action>>
</root>
Под <</action>> понимается имя тега,
соответствующему экшену в модуле, например, для экшена edit буде тег
<edit> . Но разработка структуры - дело сугубо
индивидуальное.
В заключение хотелось бы заметить, что при использовании XSLT-процессора
sablotron (версия php 4) необходимо определить базовую директорию, где лежат
файлы включений, функцией xslt_set_base($xh, $filebase) , где
переменная $filebase должна содержать полный путь к директории.
Если что-то не получается.
К сожалению, существующие XSLT-процессоры не имеют средств отладки.
Приходится применять «дедовские способы» и «идти по шагам к цели», т.е. идти от
разработки общих шаблонов к разработке частных шаблонов. Иначе это называют
проектированием сверху вниз.
Как правило, разработка шаблонов соответствует порядку разбора шаблонов, т.е.
начинается с корневого элемента xml-данных. Может само обрамление дизайна – это
уже дело рук самого дизайнера, но заготовку в виде пустого шаблона
<xsl:template match="root"> надо предусмотреть. И пошли так
далее, углубляясь по ходу обработки структуры xml- данных.
Если шаблон ничего не выводит, то надо проверить:
- вызывается ли вообще данный шаблон (применяется ли к нему шаблонное
правило)
- наличие данных, обрабатываемых данным шаблоном
- результат преобразования.
Первое (вызывается ли вообще данный шаблон) проверяется довольно просто
заменой нашего шаблона конструкцией типа: <xsl:template match="item">
**********
</xsl:template >
Если шаблон вызывался (применялось к нему шаблонное правило), то в
результирующем документе увидим наши звездочки.
Несколько труднее определить, какие входные узлы обрабатывал наш шаблон. Это
определяется либо копированием обрабатываемых узлов в результирующее
преобразование, либо определением имени обрабатываемого тега.
Копирование узла осуществляется заменой нашего шаблона на следующую
конструкцию: <xsl:template match="item">
<node>
<xsl:copy-of select="."/>
</node>
</xsl:template >
Результатом применения будет текст, содержащий значение текущего узла,
обрамленного тегами <node> .
Определение имени обрабатываемого узла осуществляется заменой нашего шаблона
на такой: <xsl:template match="item">
<name>
<xsl:value-of select="name(.)"/>
</name>
</xsl:template >
Результатом применения будет текст, содержащий имя текущего узла,
обрамленного тегами <name> .
Иногда полезно знать положение узла в контексте, тогда применим функцию
position() к текущему узлу. Использование этой функции позволяет
нам увидеть динамику прохода, т.е. применение шаблонных правил к текущей ветке
xml-документа. Как правило, функцию position() применяют в циклах
<xsl:for-each> , но иногда применяют и к вызовам шаблонных
правил.
Проверка результата преобразования - это моделирование части работы шаблона
(или всего полностью). Как и всякое моделирование, оно заключается в подготовке
входных данных, которые должны представлять часть нашего xml-документа, самого
шаблонного правила, т.е. набора части задействованных шаблонов, и,
соответственно, простого скрипта преобразования, суть действий которого – взять
файл xml-документа с диска, файл xsl-шаблона и вывести результат
преобразования.
Ближе к практике. Сортировка.
Одна из часто применяемых практических задач – это вывод отсортированной
таблицы, причем критерий сортировки определен данными, т.е. при разработке
шаблона мы заранее знаем только перечень критериев, по которым будет
производиться сортировка. Для примера возьмем прайс-лист, который можно
отсортировать по:
- цене
- наименованию товара
- наименованию группы товаров
Если в шаблоне не запланировано вывода нескольких таблиц, то имя критерия
сортировки можно содержать в корневом элементе. В случае, если таблиц несколько,
то имя критерия сортировки, можно хранить в атрибуте корневого элемента
фрагмента данных таблицы. Лично я использую для этого корневой элемент.
Допустим, имеется следующая структура данных: <root sort="type">
. . .
<table>
<item price="12" type="Сувениры" name="Сувенир Мозайка">
<item price="13" type="Сувениры" name="Сувенир Шкатулка">
. . .
<item price="85" type="Фотопленки" name="Пленка 12/200 Kodak">
</table>
</root>
Значение атрибута sort корневого элемента root
определяет, какой тип сортировки использовать. В данном примере используется
сортировка по категориям товаров (type).
Шаблон, обрабатывающий тег <table> и «строящий» таблицу,
будет выглядеть следующим образом: <xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="root/table" />
</body>
</html>
</xsl:template >
<xsl:template match="table">
<xsl:variable name="sort" select="//root/@sort"/>
<table>
<xsl:for-each select="item">
<xsl:sort select="@name[$sort='name']"/>
<xsl:sort select="@price[$sort='price']"/>
<xsl:sort select="@type[$sort='type']"/>
<tr>
<td><xsl:value-of select="@name"/></td>
<td><xsl:value-of select="@type"/></td>
<td><xsl:value-of select="@price"/></td>
</tr>
</xsl:for-each >
</table>
</xsl:template >
Для реализации данного шаблона использован XSLT-элемент
<xsl:variable> , который определяет XSLT переменную
$sort . Данная переменная содержит значение атрибута
sort корневого элемента root .
Для определения критерия сортировки использовались предикаты выборки. При
вычислении предиката его результат приводится к булевому типу. Синтаксически
предикат заключен в квадратные скобки, т.е. конструкция типа <xsl:sort
select="@type[$sort='type']"/> показывает, что сортировка будет
осуществляться для всех узлов по ключу, соответствующему атрибуту
@type , при условии, что значение переменной $sort
будет равно стоковому выражению «type». Соответственно, по каждому критерию, по
которому будет осуществлена сортировка, необходима своя команда
<xsl:sort> .
Заключение
Конечно, количество всяких методов и приемов при разработке XSLT шаблонов
множество и «нельзя объять необъятное». В настоящее время даже появились разные
стили написания шаблонов. Я попытался охватить лишь первые этапы «Путешествия в
страну Шаблонизации».
В данной статье были отражены в основном главные проблемы, с которыми
сталкиваются юные XSLT-шаблонизаторы. Планируется продолжение.
Уже после написания данной статьи автор узнал о существовании инструментария
XSLT-разработчика, включающее средства XSLT-отладки. Это такие программные
продукты, как: xselerate, Stylus Studio, Altova XML StyleVision. Данные
программные продукты являются платными.
Автор будет благодарен за любую критику данного материала, а также будет рад
услышать ваши пожелания и узнать, с какими же вы сталкивались трудностями при
разработке XSLT–шаблонов. Все замечания будут учтены при подготовке следующей
статьи из цикла: «Шаблонизация на XSLT».
Автор: Александр Календарев
Перевод: www.detail.phpclub.ru
|