XSLT первый шаг

  1. Введение
  2. Валидный XHTML
  3. XSLT-преобразования
  4. Приложение

1. Введение

Не прошло и трёх лет с тех пор, как у меня зародилась мысль о том, что пора изучать XSLT -))). Мысль зародилась, а везде ещё стоял PHP 4 и зверствовал Salbotron, который, мягко говоря, не отличался высокой производительностью. Да и редко какой браузер мог похвастаться поддержкой этого самого XSLT. По этим соображениям изучение столь перспективного направления я отложил до лучших времён. На данный момент можно смело заявить, что эти времена настали, поскольку вышел PHP 5 с поддержкой XSLT и сносной объектной моделью, а все топовые браузеры уже сами уверенно держат преобразования, только подавай XML. :)

Важные ссылки по теме, первоисточники:

Переводы на русский язык:

Для лучшего понимания всего происходящего я рекомендую читать спецификации в следующем порядке:

  1. XML (это основа!)
  2. пространства имён (механизм разнородного XML-кода в одном файле)
  3. XPath (язык выборки элементов из дерева структуры)
  4. XSLT (преобразования)
  5. XHTML (то, к чему нужно стремиться)

Особо пытливые могут также уделить внимание расширенному языку стилей XSL.

2. Валидный XHTML

Что такое валидный XHTML? В первую очередь, это XML-документ, который должен соответствовать спецификации XML. Во-вторую, почти обычная HTML-страница, к которой все привыкли.

Почему нужен именно XHTML? Исключительно из соображений совместимости и кросс-браузерности. Страница в XHTML будет с большей вероятностью отображаться корректно в популярных браузерах, чем обычный HTML.

Для рядового клепателя страниц словосочетание XML-документ должно означать следующее:

  1. Документ содержит объявление XML-документа в самом начале страницы:
    <?xml ... ?>
  2. Документ содержит один корневой элемент, в котором находятся все остальные.
  3. Все элементы (тэги) должны иметь закрывающую часть (<br />, <p>...</p>).
  4. Атрибуты всегда имеют значение, которое обязательно указывается в кавычках (одинарных или двойных). Например, <input type="radio" disabled="disabled" />.
  5. Управляющие символы &, < и > всегда должны маскироваться. Например, <a href="?a=1&amp;b=2">&amp;</a>. Исключение составляет только <![CDATA[...]]>, внутри которого спецсимволы можно не маскировать.

Также сам XHTML обязывает выполнять следующие условия:

  1. Документ должен объявлять пространство имён, в рамках которого будут использоваться элементы HTML.
  2. Документ должен объявлять DOCTYPE перед корневым элементом и указывать в нём один из типов XHTML и соответствующий DTD.

Пример простого документа XHTML1.0:

Код - валидный xhtml
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
  <head>
    <title>Это валидный XHTML!</title>
  </head>
  <body>
    <p>Привет, мир!</p>
  </body>
</html>

И так обо всём по порядку.

  1. Объявление XML-документа, в котором указывается его версия и кодировка.

    <?xml version="1.0" encoding="windows-1251"?>

    Для большей безопасности кодировку нужно всегда выставлять, иначе могут возникнуть проблемы с невалидными (по отношению к дефолтной кодировке) символами.

  2. Объявление типа документа и его схемы.

    <!DOCTYPE html 
         PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    Для XHTML 1.0 есть три типа - Strict (строгое соответствие рекомендациям W3C), Transitional (переходный тип) и Frameset (использование фреймов). Для каждого из них предусмотрен отдельный DTD.

  3. Объявление пространства имён и используемого языка.

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">

    Очень важно указывать ссылку именно в таком регистре и никак иначе. Это связано с тем, что в XML имена элементов и содержимое их атрибутов регистрозависимы.

Три версии XHTML1.0 предназначены для лучшей обратной совместимости:

Помимо XHTML1.0 на данный момент доступен XHTML1.1:

<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
  <head>
    <title>XHTML1.1</title>
  </head>
  <body>
    <p>Это валидный XHTML1.1!</p>
  </body>
</html>

XHTML1.1 по сути является тем же XHTML1.0 Strict и призван вытеснить другие версии XHTML1.0. Однако, по сравнению с XHTML1.0 Strict, у него есть ряд отличий:

  1. Удалён атрибут lang, его роль выполняет xml:lang. (Модуль [XHTMLMOD])
  2. Для элементов a и map вместо атрибута name нужно использовать атрибут id. (Модуль [XHTMLMOD])
  3. Доступен набор элементов ruby. (Модуль [RUBY])

Итак, если вам нужна наибольшая кросс-браузерность и совместимость с рекомендациями W3C, то XHTML1.1 самое оно!

Из этих соображений результатом моих преобразований будет именно XHTML1.1.

3. XSLT-преобразования

Что такое XSLT? Это язык преобразований XML-документа, который был разработан как часть расширенного языка стилей (XSL).

Зачем нужен XSLT? Он позволяет реализовать схему, при которой данные хранятся отдельно, а их представление отдельно. То есть, один XML-документ преобразуется с помощью другого XML-документа (XSL, в котором находятся XSLT-шаблоны) в конечный документ. Результатом может быть XML, HTML или текстовый документ любого формата.

Для того, чтобы воспользоваться XSLT-преобразованиями, в первую очередь нужно сформировать правильный стиль XSL и подключить его к XML-файлу.

Валидным XSL-документом является XML-документ, у которого задано пространство имён xsl и присутствует корневой элемент stylesheet. В самом простом случае стиль может выглядеть, например, так:

Файл - test.xsl
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

</xsl:stylesheet>

Этот стиль не содержит каких-либо явных определений шаблонов или других элементов XSL. Однако, его уже можно использовать. Чтобы посмотреть результат, достаточно сформировать произвольный XML-документ и подключить к нему этот стиль:

Файл - test.xml
<?xml version="1.0" encoding="windows-1251"?>
<?xml-stylesheet type='text/xsl' href='test.xsl'?>
<elements attr1="Главный атрибут">
  <element attr1="мой атрибут1" attr2="мой атрибут2">Один</element>
  <element>Два</element>
  <element attr5="Халявный атрибут">Три</element>
</elements>

За подключение стиля отвечает строка:

<?xml-stylesheet type='text/xsl' href='test.xsl'?>

Если файлы text.xml и test.xsl созданы и находятся в одной папке, то с помощью любого XSLT-парсера можно преобразовать исходный test.xml в результирующий документ. В качестве парсера могут выступать все популярные браузеры (IE5+, FF2+, Opera9+ и другие), а также модули в языках программирования, например, в PHP. Если вы используете браузер, то достаточно открыть test.xml, и он сразу отобразит примерно такой результат:

Результат
Один Два Три

При этом кодировка результата будет UTF-8, несмотря на то, что исходный документ был сформирован в windows-1251. К сожалению, браузеры обычно не позволяют просмотреть код результирующего документа, но модуль XSLT в PHP5 даёт возможность передать результирующий код в переменную, которую можно сохранить в файл. Поэтому, используя PHP, я приведу исходный код результирующего документа:

Результат - исходный код
1
2
3
4
5
6
7
<?xml version="1.0"?>

Один
Два
Три

Этот код не является валидным XML-документом и тем более XHTML1.1. Для того, чтобы сформировать нужный код, я усложню исходный XSL-стиль и добавлю туда необходимые шаблоны и преобразования. При этом исходный XML-документ останется без изменений.

В качестве примера я приведу XSL-стиль, который при помощи XSLT будет выводить список атрибутов исходного XML-документа с их значениями, при этом будет формироваться валидный XHTML1.1. Итак, стиль:

Файл - test.xsl
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml"
  encoding="windows-1251"
  omit-xml-declaration="no"
  indent="yes"
  media-type="text/xml"
  doctype-public="-//W3C//DTD XHTML 1.1//EN"
  doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
/>

<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
<head>
  <title>Мой первый XSLT</title>
</head>
<body>
  <div><xsl:text>Мой список:</xsl:text></div>
  <ol>
    <xsl:for-each select="/descendant-or-self::*/@*">
      <li>
        <xsl:if test="position() mod 2 = 0">
          <xsl:attribute name="style">background-color: #eee;</xsl:attribute>
        </xsl:if>
        <xsl:value-of select="concat(name(), ' = ', .)" />
      </li>
    </xsl:for-each>
  </ol>
  <div>
    <xsl:text>Разработчик парсера: </xsl:text>
    <a>
      <xsl:attribute name="href">
        <xsl:value-of select="system-property('xsl:vendor-url')" />
      </xsl:attribute>
      <xsl:attribute name="title">
        <xsl:value-of select="system-property('xsl:vendor-url')" />
      </xsl:attribute>
      <xsl:value-of select="system-property('xsl:vendor')" />
    </a>
  </div>
</body>
</html>
</xsl:template>

</xsl:stylesheet>

Чтобы понять, как он работает, я распишу каждое действие отдельно:

  1. Объявление XML-документа:

    <?xml version="1.0" encoding="windows-1251"?>

    Документ сформирован в кодировке windows-1251, о чём сообщается в атрибуте encoding. Версию XML-документа желательно всегда указывать, это рекомендация W3C.

  2. Затем идёт объявление корневого элемента, стиля:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    Обязательным атрибутом является определение пространства имён xsl через атрибут xmlns:xsl="http://www.w3.org/1999/XSL/Transform".

  3. Следующим шагом в корневом элементе stylesheet объявляется, каким образом нужно формировать результирующий документ:

    <xsl:output 
      method="xml"
      encoding="windows-1251"
      omit-xml-declaration="no"
      indent="yes"
      media-type="text/xml"
      doctype-public="-//W3C//DTD XHTML 1.1//EN"
      doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
    />

    Основные атрибуты:

    • method="xml" - метод вывода документа. Результирующий документ будет в формате XML.
    • encoding="windows-1251" - кодировка результирующего документа.
    • omit-xml-declaration="no" - пропускать или нет начальное объявление XML-документа (<?xml ... ?>). Может иметь значение "yes" или "no" (актуально только для html).
    • indent="yes" - формировать отступы согласно уровню вложенности. Может иметь значение "yes" или "no".
    • media-type="text/xml" - MIME-тип результирующего документа (используется только для метода вывода html).
    • doctype-public="-//W3C//DTD XHTML 1.1//EN" - тип результируюшего документа (DOCTYPE)
    • doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" - ссылка на DTD

    Если метод вывода объявлен html, то значения атрибутов encoding и media-type будут подставлены в заголовок страницы (<head>...</head>) посредством метатега.

  4. Объявление основного шаблона:

    <xsl:template match="/">

    Именно этот XSLT-шаблон соответствует корню исходного дерева и будет вызван первым для преобразования. Атрибут match принимает значения, которые должны соответствовать языку поиска элементов XPath.

    Остальные шаблоны, если таковые имеются, должны подключаться из этого шаблона при помощи средств XSLT.

  5. Формирование XHTML-страницы. Оно начинается с элемента <html>, у которого указано пространство имён xhtml:

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">

    Атрибут xmlns="http://www.w3.org/1999/xhtml" указывает на пространство имён xhtml, которое будет применено по умолчанию к этому элементу и всем дочерним элементам, у которых оно не задано явно.

    Атрибут xml:lang="ru" указывает на язык, в котором сформирована страница (будущая).

Эта часть стиля была нужна для формирования атрибутики валидного XHTML1.1 кода.

Теперь что касается XSLT-преобразований:

  1. Вставка простого текста:

    <div><xsl:text>Мой список:</xsl:text></div>

    Текст "Мой список:" будет подставлен в тег <div> с маскированием управляющих символов. В принципе, этот код ничего особенно не делает, а стоит просто, как пример.

  2. Организация цикла по выборке:

    <xsl:for-each select="/descendant-or-self::*/@*">

    Атрибут select принимает выражение XPath, на основе которого делает выборку. Если выборка вернула список узлов, то начинает работать цикл по каждому элементу.

    В данном случае выборка вернёт список атрибутов для этого (корневого) и всех дочерних элементов.

  3. Проверка условия:

    <xsl:if test="position() mod 2 = 0">

    В данном случае проверяется на чётность позиция элемента в списке выборки. Если тест возвращает true (порядковый номер элемента чётный), то срабатывает содержимое этого элемента.

  4. Управление атрибутами вышестоящего элемента:

    <xsl:attribute name="style">background-color: #eee;</xsl:attribute>

    В данном случае, если позиция элемента чётная (определяется вышестоящим if), то в стиль элемента <li> будет прописан серый цвет фона.

  5. Вывод значений элемента:

    <xsl:value-of select="concat(name(), ' = ', .)" />

    Этот код подставит в вышестоящий элемент строку, собранную из имени текущего элемента и его значения. Содержимое атрибута select соответствует XPath.

  6. Вывод ссылки на разработчика парсера XSLT:

        <xsl:text>Разработчик парсера: </xsl:text>
        <a>
          <xsl:attribute name="href">
            <xsl:value-of select="system-property('xsl:vendor-url')" />
          </xsl:attribute>
          <xsl:attribute name="title">
            <xsl:value-of select="system-property('xsl:vendor-url')" />
          </xsl:attribute>
          <xsl:value-of select="system-property('xsl:vendor')" />
        </a>

    Этот небольшой код XSLT формирует ссылку на разработчика парсера XSLT. Во многих случаях она будет разная и содержать разные значения.

Результатом обработки этого стиля (test.xsl) станет такой код:

Результат - исходный код
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
  <head>
    <title>Мой первый XSLT</title>
  </head>
  <body>
    <div>Мой список:</div>
    <ol>
      <li>attr1 = Главный атрибут</li>
      <li style="background-color: #eee;">attr1 = мой атрибут1</li>
      <li>attr2 = мой атрибут2</li>
      <li style="background-color: #eee;">attr5 = Халявный атрибут</li>
    </ol>
    <div>Разработчик парсера: <a href="http://xmlsoft.org/XSLT/" title="http://xmlsoft.org/XSLT/">libxslt</a></div>
  </body>
</html>

Этот код соответствует стандарту XHTML1.1 и был сформирован на основе исходного XML-документа. Для проверки можно воспользоваться валидатором от W3C, который расположен по адресу http://validator.w3.org/.

В браузере этот код выглядит примерно так:

IE 6 FireFox 3 Opera 9.02
ie ff3 opera

4. Приложение

Ссылки на исходный код

Файл с данными test.xml доступен по адресу //anton-pribora.ru/articles/xml/xslt-first-step/test.xml.

Файл со стилем test.xsl доступен по адресу //anton-pribora.ru/articles/xml/xslt-first-step/test.xsl.

Исходный код примера на PHP5 //anton-pribora.ru/articles/xml/xslt-first-step/test.phps.

Постоянный адрес статьи //anton-pribora.ru/articles/xml/xslt-first-step/. /Автор: Прибора Антон Николаевич, 2009 год/

Использование PHP5 для обработки XSLT

Для получения результирующего документа при помощи PHP5 я использовал такой код:

Пример - использование XSLT в PHP5
<?php
// Вывод кода HTML в виде текста
header('Content-Type: text/plain;');

// Объект исходного XML-документа
$xml = new DOMDocument(null, 'windows-1251');
$xml->load('test.xml');

// Объект стиля
$xsl = new DOMDocument(null, 'windows-1251');
$xsl->load('test.xsl');

// Создание парсера
$proc = new XSLTProcessor();

// Подключение стиля к парсеру
$proc->importStylesheet($xsl);

// Обработка парсером исходного XML-документа
$parsed = $proc->transformToXml($xml);

// Вывод результирующего кода
echo $parsed;

// Запись файла с результирующим кодом
file_put_contents('parsed.html', $parsed);

?>

Дополнительную информацию по использованию XSLT в PHP5 можно найти по адресу http://ru2.php.net/manual/ru/book.xslt.php.

Мысли вслух

«Товарищи, мы стоим на краю огромной пропасти! И я предлагаю сделать большой, решительный шаг вперёд!»