Работаем с XSLT.
Автор: Daniel Guerrero
Перевод: Андрей Киселев
XSLT (от англ. eXtensible Stylesheet Language for Transformations -- Расширяемый Язык Стилей для Преобразований) используется, по большей части, для преобразования данных из формата XML в формат HTML. Однако, XSLT может использоваться для преобразования из XML (или любого другого формата, использующего пространство имен xml, подобно RDF) в любой другой формат, даже в простой текст.
Консорциум W3 определяет три составные части языка XSL (от англ. eXtensible Stylesheet Language -- Расширяемый Язык Стилей): XSLT, XPath (язык путей и выражений, используемый в XSLT для доступа к отдельным частям XML-документа) и XSL Formatting Objects -- словарь, определяющий семантику форматирования документов.
Прежде всего следует указать, что наш документ использует стилистику XML и импортировать пространство имен XML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> ... </xsl:stylesheet>
Далее, основным элементом, который мы будем использовать, является xsl:template
match
. Этот элемент вызывается всякий раз, когда имя xml-узла совпадает
со значением атрибута xsl:template match
:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <!-- оператор '/' взят из XPath и ассоциируется с корневым элементом --> <!-- выполнить какие либо действия с вложенными узлами --> </xsl:template> </xsl:stylesheet>
Внутри элемента xsl:template match
следует указать вложенные узлы
элементом: xsl:value-of select
. Давайте для начала создадим xml-документ,
содержащий некоторую информацию:
<!-- hello.xml --> <hello> <text>Hello World!</text> </hello>
Так должно выглядеть xslt-преобразование, которое вынимает узел text
из корневого элемента (hello
):
<!-- hello.xsl --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="//text"/> </title> <!-- в данном случае '//text' это: 'hello/text', но, поскольку я ленив сам по себе, я делаю это проще, используя выражение XPath --> </head> <body> <p> Содержимое узла <b>text</b> корневого элемента: <b><xsl:value-of select="//text"/></b> </p> </body> </html> </xsl:template> </xsl:stylesheet>
В результате получится следующий HTML-документ:
<!-- hello.html --> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Hello World! </title> </head> <body> <p> Содержимое узла <b>text</b> корневого элемента: <b>Hello World!</b> </p> </body> </html>
конструкция @att
возвращает значение атрибута att
.
Например:
<!-- hello_style.xml --> <hello> <text color="red">Hello World!</text> </hello>
XSLT-преобразование:
<!-- hello_style.xsl --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <title> <xsl:value-of select="//text"/> </title> </head> <body> <p> Содержимое узла <b>text</b> корневого элемента: <b><xsl:value-of select="//text"/></b> и его атрибут <b>color</b> : <xsl:value-of select="//text/@color"/> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Результирующий HTML-документ:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Hello World! </title> </head> <body> <p> Содержимое узла <b>text</b> корневого элемента: <b>Hello World!</b> и его атрибут <b>color</b> : red </p> </body> </html>
Если вы задумаете использовать атрибут color для вывода текста Hello World!
соответствующим цветом, то сделать это можно двумя способами: создать переменную
и использовать ее для задания цвета шрифта или воспользоваться элементом xsl:attribute
.
Переменные в XSLT отличается от переменных в обычных языках программирования из-за того, что их значения не могут изменяться. После того как переменной присвоено какое-то значение, оно остается постоянным.
(Странно, почему перменные названы переменными, а не константами. Прим.ред.)Определяются переменные просто:
<!-- variables.xsl --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <!-- definition of the variable --> <xsl:variable name="path">http://somedomain/tmp/xslt</xsl:variable> <html> <head> <title>Пример с переменными</title> </head> <body> <p> <a href="{$path}/photo.jpg">Фотография моего последнего путешествия</a> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Результирующий HTML-документ:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Пример с переменными</title> </head> <body> <p><a href="http://somedomain/xslt/photo.jpg">Фотография моего последнего путешествия</a></p> </body> </html>
Переменной можно присвоить значение узла или значение атрибута узла:
<!-- variables_select.xsl --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <title>Пример с переменными</title> </head> <body> <xsl:apply-templates select="//photo"/> </body> </html> </xsl:template> <xsl:template match="photo"> <!-- определение переменных --> <xsl:variable name="path">http://somedomain/tmp/xslt</xsl:variable> <xsl:variable name="photo" select="file"/> <p> <a href="{$path}/{$photo}"><xsl:value-of select="description"/></a> </p> </xsl:template> </xsl:stylesheet>
Исходный xml-документ (я не стал сопровождать статью своими фотографиями, чтобы не напугать вас :-) )
<!-- variables_select.xml --> <album> <photo> <file>mountains.jpg</file> <description>я - в горах</description> </photo> <photo> <file>congress.jpg</file> <description>я - на конгрессе</description> </photo> <photo> <file>school.jpg</file> <description>я - в школе</description> </photo> </album>
Результирующий HTML-документ:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Пример с переменными</title> </head> <body> <p><a href="http://somedomain/tmp/xslt/mountains.jpg">я - в горах</a></p> <p><a href="http://somedomain/tmp/xslt/congress.jpg">я - на конгрессе</a></p> <p><a href="http://somedomain/tmp/xslt/school.jpg">я - в школе</a></p> </body> </html>
Как вы могли заметить, элемент <xsl:template match="photo"> был вызван
трижды, это произошло потому, что всякий раз, когда xslt обнаруживает элемент,
совпадающий с xsl:apply-templates
, он вызывает соответствующий
xsl:template match
.
Итак! Вам не терпится вывести текст красным цветом? Попробуйте сделать это с помощью переменных, если не получится, то можете заглянуть в пример hello_style_variables.xsl
Сортировка XML-тегов в XSLT выполняется посредством элемента <xsl:sort
select="sort_by_this_attribute">
Этот элемент должен размещаться
внутри xsl:apply-templates
(сортировка может производиться
так же и в элементе xsl:for-each
прим. перев.).
Сортировка может выполняться как по самим xml-тегам, так и по их атрибутам,
порядок сортировки можно задавать по возрастанию или по убыванию (если символы
нижнего регистра должны предшествовать символам верхнего регистра или наоборот).
Для демонстрации сортировки я использовал пример альбома с фотографиями, в который добавил элемент <xsl:sort>:
<xsl:apply-templates select="//photo"> <xsl:sort select="file" order="descending"> </xsl:apply-templates>
Здесь изменен порядок следования фотографий в выходном html-документе. Теперь
xslt сначала упорядочит все элементы photo
из xml-файла, а затем
передаст их элементу template-match
, вот почему xsl:sort
должен находиться внутри элемента xsl:apply-templates
.
Файлы xsl и html примера вы можете взять здесь:
Иногда возникает необходимость поместить в выходной документ некоторый текст,
если задан какой-либо xml-элемент (или его атрибут), либо другой текст, если
этот элемент (или атрибут) отсутствует. В таких случаях можно использовать элемент
xsl:if
. Я продемонстрирую вам как это делается (этот пример взят
из моих наработок в проекте TLDP-ES). Если вам известно, что некий исходный
документ был преобразован в формат PDF, PS или HTML, то это обстоятельство можно
отразить в xml-файле, т.е. если был создан PDF-файл, то в выходной html-файл
вставляется ссылка на него:
<xsl:if test="format/@pdf = 'yes'"> <a href="{$doc_path}/{$doc_subpath}/{$doc_subpath}.pdf">PDF</a> </xsl:if>
Если атрибуту pdf документа присвоено значение "yes", как показано в примере:
<document> <title>Bellatrix Library and Semantic Web</title> <author>Daniel Guerrero</author> <module>bellatrix</module> <format pdf="yes" ps="yes" html="yes"/> </document>
То в выходной html-файл будет вставлена ссылка на документ в PDF-формате. Если атрибуту присвоено значение "no" или любое другое, допустимое вашим преобразованием, значение, то ссылка не будет вставлена. Все вышесказанное вы можете увидеть в xsl и xml документах:
Если вы внимательно посмотрите на xml-документ, приведенный выше, то заметите,
что авторы представлены в виде списка имен, разделенных запятыми. Очевидно,
что наилучшим выходом было бы поместить имена авторов в отдельные теги <author>
:
<document> <title>Donantonio: bibliographic system for automatic distribuited publication. Specifications of Software Requeriments</title> <author>Ismael Olea</author> <author>Juan Jose Amor</author> <author>David Escorial</author> <module>donantonio</module> <format pdf="yes" ps="no" html="yes"/> </document>
И вывести каждое имя в отдельной строке с помощью xsl:apply-templates
и xsl:template match
, но то же самое можно сделать и с помощью
инструкции xsl:for-each
.
<xsl:for-each select="author"> <tr> <td> Author: <xsl:apply-templates /> </td> </tr> </xsl:for-each>
В этом случае XSLT-процессор пройдет по списку авторов документа и, если вас
интересует какой шаблон я использовал для обработки тегов <author>, я
могу сказать - никакой. XSLT-процессор воспримет элемент apply-templates
как обычный 'print' и выведет содержимое тега, выбранного элементом for-each
.
Последний xslt-элемент, который я хочу вам продемонстрировать, это элемент
choose. Он очень похож на инструкцию switch
языка программирования
C.
Первым должен идти элемент xsl:choose
, а за ним дополнительные
(один или несколько) элементы xsl:when
, если требуется обрабатывать
значение не подпадающее ни под одно из условий имеющихся элементов xsl:when
,
то вы можете добавить элемент xsl:otherwise
:
<xsl:variable name="even" select="position() mod 2"/> <xsl:choose> <xsl:when test="$even = 1"> <![CDATA[<table width="100%" bgcolor="#cccccc">]]> </xsl:when> <xsl:when test="$even = 0"> <![CDATA[<table width="100%" bgcolor="#99b0bf">]]> </xsl:when> <xsl:otherwise> <![CDATA[<table width="100%" bgcolor="#ffffff">]]> </xsl:otherwise> </xsl:choose>
Функция position()
возвращает порядковый номер обрабатываемого
элемента, в нашем случае -- документа. В данном примере нас интересует только
четность порядкового номера, тем самым мы получаем возможность выделять четные
и нечетные строки таблицы различным цветом. Я поместил элемент xsl:otherwise
исключительно в демонстрационных целях, фактически же вы никогда не увидите
строку с белым фоном в нашей таблице.
Если вы спросите меня зачем я вставил секцию CDATA
, то я вам отвечу,
если бы я этого не сделал, то XSLT-процессор генерировал бы сообщения об ошибке
по поводу отсутствия закрывающего тега (</table>
), но в нашем
случае этот тег находится ниже. По той же самой причине, закрывающий тег </table>
так же должен быть оформлен в виде секции CDATA
.
Я привел лишь короткий отрывок из примера, полный текст файлов xsl и html вы найдете по ссылкам:
Процессор Saxon написан на языке Java, я пользуюсь версией 6.5.2. Все нижеследующие инструкции касаются этой версии, если у вас другая версия, то вам следует обратиться к документации для вашей версии за получением информации по установке и запуску процессора.
После того как вы скачаете архив с процессором saxon вам нужно распаковать его:
[danguer@perseo xslt]$ unzip saxon6_5_2.zip
Затем, вам нужно добавить файл saxon.jar к пути поиска классов, путь к jar-архиву
можно передать с помощью ключа -cp path
(можно добавить путь
к jar-файлу в переменную окружения CLASSPATH прим. перев.).
Я поместил файл saxon.jar в каталог xslt, кроме того необходимо передать Java
используемый класс, в случае Saxon 6.5.2 используется класс com.icl.saxon.StyleSheet
и затем должны следовать xml-документ и xsl-файл, например:
[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet document.xml tranformation.xsl
Эта команда отправит результат работы процессора на устройство стандартного вывода (STDOUT), перенаправить вывод в файл можно так:
[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet document.xml tranformation.xsl > file_processed.html
Например мы можем преобразовать наш первый пример XSLT с помощью процессора saxon:
[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet hello.xml hello.xsl > hello.html
Процессор xsltproc включен в состав большинства дистрибутивов, синтаксис вызова похож на вызов процессора saxon:
[danguer@perseo xslt]$ xsltproc hello.xsl hello.xml > hello.html
Я знаю о существовании и других процессоров, таких как sablotron, но я ими не пользовался, а потому не могу рекомендовать их вам ;-).
Daniel Guerrero
Я заканчиваю обучение на степень бакалавра BUAP в городе Пуэбло (Puebla), Мексика. Учавствую в работе проекта TLPD-ES (испанский вариант The Linux Documentation Project прим. перев.) все мои познания об этих технологиях я приобрел здесь. В настоящий момент я изучаю Web-семантику.Copyright (C) 2003, Daniel Guerrero.