Главная » XSLT » Генерация XSLT из XSLT

0

Задача

Требуется сгенерировать XSLT-код из другого XML-представления. Или пре­образовать один код на XSLT или псевдо-XSLT в другой.

Решение

Иногда меня раздражают два аспекта управляющих конструкций в XSLT. Первый – отсутствие конструкции if-then-elsif-else; второй – отсутствие настоящих циклов. Конечно, я знаю о существовании команд xsl:choose и xsl:for-each, но у каждой из них есть недостатки. Так, xsl:choose мне не нравится, потому что у элемента choose нет никакого иного назначения, кроме организации дополнительного уровня вложенности. А xsl:for-each – не столько цикл, сколько конструкция для итерирования. Чтобы эмулировать поведение цикла со счетчиком, приходится прибегать к рекурсии или методу Пиза (см. рецепт 2.5), а это не красиво.

В следующем примере иллюстрируется преобразование в XSLT кода, напи­санного на псевдо-XSLT, в котором имеются элементы xslx:elsif, xslx:else и xslx:loop. Поскольку на самом деле их нет, то мы создадим таблицу стилей, кото­рая заменит эти элементы реально существующими конструкциями. Одновременное наличие элементов xsl:if и xslx:if выглядит нелепо, но было бы неправиль­но использовать стандартное пространство имен XSLT для дополнительных элементов; не исключено, что в будущей версии стандарта XSLT они будут опре­делены.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslx="http://www.ora.com/XSLTCookbook/ExtendedXSLT" >

<xsl:output method="text"/>

<xsl:template match="foo"> <xslx:if test="bar">

<xsl:text>Hередко рядом с bar встречается и foo!</xsl:text> </xslx:if>

<xslx:elsif test="baz">

<xsl:text>Слово baz – верный признак сленга</xsl:text> </xslx:elsif> <xslx:else>

<xslx:loop param="i" init="0" test="$i &lt; 10" incr="1">

<xsl:text>Гм, сказать-то нечего, но повторю это 10 раз.</xsl:text> </xslx:loop> </xslx:else>

<xslx:loop param="i" init="10" test="$i >= 0" incr="-1">

<xslx:loop param="j" init="10" test="$j >= 0" incr="-1"> <xsl:text>&#xa;</xsl:text> <xsl:value-of select="$i * $j"/> </xslx:loop> </xslx:loop> <xslx:if test="foo">

<xsl:text>foo foo! Никто не говорит foo foo!</xsl:text> </xslx:if> <xslx:else>

<xsl:text>Hу и ладно!</xsl:text> </xslx:else>

</xsl:template>

</xsl:stylesheet>

А ниже приведена таблица стилей, которая генерирует настоящий XSLT-код из такого псевдокода. Для простоты мы не включили некоторые семантические проверки, например, наличие нескольких xsl:else при единственном xsl:if или дублирование параметров во вложенных циклах:

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslx="http://www.ora.com/XSLTCookbook/ExtendedXSLT"

xmlns:xso="dummy" >

<!– Повторно используем тождественное преобразование –> <!– для копирования корректного XSLT-кода –> <xsl:import href="../util/copy.xslt"/>

<!– НЕ позволяем процессору выполнять форматирование с –>

<!– помощью indent = yes, так как это испортило бы узлы xsl:text –>

<xsl:output method="xml" version="1.0" encoding="UTF-8" />

<!– Когда требует вывести элементы xslt буквально, применяем –> <!– псевдоним xso –>

<xsl:namespace-alias stylesheet-prefix="xso" result-prefix="xsl"/>

<xsl:template match="xsl:stylesheet | xsl:transform"> <xso:stylesheet>

<!– На первом проходе транслируем if-elsif-else —>

<!– и преобразуем xslx:loop в вызовы именованных шаблонов –>

<xsl:apply-templates select="@* | node( )"/>

<!– На втором проходе преобразуем xslx:loop в рекурсивные –> <!– именованные шаблоны –>

<xsl:apply-templates mode="loop-body" select="//xslx:loop"/>

</xso:stylesheet> </xsl:template>

<!– Ищем элементы xslx:if, которым соответствуют элементы xslx:elsif или xslx:else –>

<xsl:template match="xslx:if[following-sibling::xslx:else or

following-sibling::xslx:elsif]"> <xsl:variable name="idIf" select="generate-id( )"/> <xso:choose>

<xso:when test="{@test}">

<xsl:apply-templates select="@* | node( )"/> </xso:when>

<!– Обрабатываем xsl:eslif и xslx:else в специальном режиме –> <!– как часть xsl:choose. Нужно выбирать только такие элементы, –> <!– для которых предшествующий элемент xslx:if совпадает с данным –> <!– xslx:if — >

<xsl:apply-templates

select="following-sibling::xslx:else[

generate-id(preceding-sibling::xslx:if[1]) = $idIf] | following-sibling::xslx:elsif[

generate-id(preceding-sibling::xslx:if[1]) = $idIf]" mode="choose"/> </xso:choose> </xsl:template>

<!— Игнорируем xslx:elsif и xslx:else в нормальном режиме —> <xsl:template match="xslx:elsif | xslx:else"/>

<!– xslx:elsif превращается в xsl:when –> <xsl:template match="xslx:elsif" mode="choose"> <xso:when test="{@test}">

<xsl:apply-templates select="@* | node( )"/> </xso:when> </xsl:template>

<!– xslx:else превращается в xsl:otherwise –> <xsl:template match="xslx:else" mode="choose"> <xso:otherwise>

<xsl:apply-templates/> </xso:otherwise> </xsl:template>

<!– xslx:loop превращается в вызов именованного шаблона –> <xsl:template match="xslx:loop">

<!– Каждому шаблону присваивается имя loop-N, где N – позиция –> <!– данного цикла относительно предшествущих циклов на –> <!– любом уровне —> <xsl:variable name="name">

<xsl:text>loop-</xsl:text>

<xsl:number count="xslx:loop" level="any"/> </xsl:variable>

<xso:call-template name="{$name}">

<xsl:for-each select="ancestor::xslx:loop">

<xso:with-param name="{@param}" select="${@param}"/> </xsl:for-each>

<xso:with-param name="{@param}" select="{@init}"/> </xso:call-template>

</xsl:template>

<!– Режим loop-body используется на втором проходе. –>

<!– Здесь генерируются рекурсивные шаблоны, реализующие цикл. –>

<xsl:template match="xslx:loop" mode="loop-body" <xsl:variable name="name"> <xsl:text>loop-</xsl:text> <xsl:value-of select="position( )"/> </xsl:variable>

<xso:template name="{$name}">

<!– Если этот цикл является вложенным, то он должен –> <!–"видеть" параметры внешнего цикла, поэтому сгенерируем их –> <xsl:for-each select="ancestor::xslx:loop">

<xso:param name="{@param}"/> </xsl:for-each>

<!– Параметр локального цикла –> <xso:param name="{@param}"/>

<!– Генерируем проверку завершения рекурсии –> <xso:if test="{@test}">

<!– Применить шаблон в нормальном режиме для обработки –> <!– обращений к вложенным циклам, а все остальное просто –> <!– скопировать. –> <xsl:apply-templates/>

<!– Это рекурсивный вызов, увеличивающий параметр цикла –> <xso:call-template name="{$name}">

<xsl:for-each select="ancestor::xslx:loop">

<xso:with-param name="{@param}" select="${@param}"/> </xsl:for-each>

<xso:with-param name="{@param}" select="${@param} + {@incr}"/> </xso:call-template> </xso:if> </xso:template> </xsl:template>

</xsl:stylesheet>

А вот результат применения этой таблицы стилей:

<xso:stylesheet xmlns:xso="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslx="http:// www.ora.com/XSLTCookbook/ ExtendedXSLT" version="1.0"> <xsl:output method="text"/> <xsl:template match="foo"> <xso:choose>

<xso:when test="bar">

<xsl:text>Hередко рядом с bar встречается и foo!</xsl:text>

</xso:when> <xso:when test="baz">

<xsl:text>Слово baz – верный признак сленга</xsl:text> </xso:when> <xso:otherwise>

<xso:call-template name="loop-1">

<xso:with-param name="i" select="0"/> </xso:call-template> </xso:otherwise> </xso:choose>

<xso:call-template name="loop-2">

<xso:with-param name="i" select="10"/> </xso:call-template> <xso:choose>

<xso:when test="foo">

<xsl:text>foo foo! Никто не говорит foo foo!</xsl:text> </xso:when> <xso:otherwise>

<xsl:text>Hу и ладно!</xsl:text> </xso:otherwise> </xso:choose> </xsl:template> <xso:template name="loop-1"> <xso:param name="i"/> <xso:if test="$i &lt; 10">

<xsl:text>Гм, сказать-то нечего, но повторю это 10 раз.</xsl:text> <xso:call-template name="loop-1">

<xso:with-param name="i" select="$i + 1"/> </xso:call-template> </xso:if> </xso:template> <xso:template name="loop-2"> <xso:param name="i"/> <xso:if test="$i &gt; = 0">

<xso:call-template name="loop-3">

<xso:with-param name="i" select="$i"/> <xso:with-param name="j" select="10"/> </xso:call-template> <xso:call-template name="loop-2">

<xso:with-param name="i" select="$i + -1"/> </xso:call-template> </xso:if> </xso:template> <xso:template name="loop-3">

<xso:param name="i"/> <xso:param name="j"/> <xso:if test="$j &gt; = 0"> <xsl:text> </xsl:text>

<xsl:value-of select="$i * $j"/> <xso:call-template name="loop-3">

<xso:with-param name="i" select="$i"/> <xso:with-param name="j" select="$j + -1"/> </xso:call-template> </xso:if> </xso:template> </xso:stylesheet>

Обсуждение

Ключом к генерации XSLT из XSLT является элемент xsl:namespace- alias. Не будь его, процессор не смог бы отличить XSLT-код исполняемой таб­лицы стилей от кода, который требуется выводить в результирующий файл. Гене­рация XSLT из XSLT бывает полезна и в других контекстах, например:

?               Включение кода в учебник.

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

?               Условные директивы include/import.

Если бы такой механизм был реализован в XSLT, то, скорее всего, потребо­валось бы вводить неуклюжие расширения в модель программирования, аналогичные препроцессору программ на C.

?               Динамическое вычисление выражений XPath.

Тут речь идет о таблице стилей, которая генерирует XPath-код из внеш­него источника и внедряет его в другую таблицу стилей, которая вычис­ляет выражения статически. Дополнительный уровень косвенности по­зволяет эмулировать динамическое поведение. Часто XPath-выражения включаются в некий документ. Например, можно встретить такое описа­ние таблицы:

<table of="person">

<column label="Firstname" content="name/firstname" /> <column label="Surname" content="name/surname" /> <column label="Age" content="@age" type="number" /> <sort select="Surname, Firstname, Age" /> </table>

Таблицу, описываемую таким XML-документом, проще сгенерировать путем создания XSLT-кода по спецификации таблицы и последующего применения его

к данным, чем путем интерпретации этой спецификации в той же таблице стилей, которая используется для обработки данных.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии