Работаем с 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.