Об авторе:
кандидат наук
эксперт в вопросах создания систем B2B
WEB разработчик компании "Агрегатор"
Прохаживаясь по страницам нашего форума (http://www.phpclub.ru/talk) и отвечая на вопросы, я пришел к выводу, что большинство спрашивающих посетителей в основных чертах знают основы применения 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) можно найти по адресу: http://citforum.ru/internet/xml/xml1_1/ или http://pyramidin.narod.ru/xml/xml1/index.htm
Использование включений
Вот уже готово ваше преобразование, осталось только придать внешний вид сгенерированной станице. Нет проблем, когда страница одна или две. Проблемы появляются, когда их больше десятка, и весь сайт надо сделать в едином стиле.
Действительно, не будешь же вносить повторяющиеся части HTML-кода в каждый шаблон. В PHP-шаблонизаторах это решалось путем включений:
%include header.tpl .... // текст шаблона ... %inclede 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:calltemplate name="item" />
.
В нашем преобразовании, должна быть инструкция <xsl:include href="main.xsl" />
и,
соответственно, шаблонное правило, определенное в «генеральном» шаблоне:
<xsl:stylesheet version="1.0" 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-парсера slabotron
(версия 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(.)
к текущему узлу. Использование 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».
Содержание выпуска