• Элемент <xsl:variable>: создание переменных
  • Область видимости переменной
  • Работа с переменными
  • Элемент <xsl:call-template>: применение именованных шаблонов
  • Элементы <xsl:param> и <xsl:with-param>: создание параметров
  • Рекурсивный вызов шаблонов
  • Шаблон: значение по умолчанию
  • Как задавать значения шаблона в командной строке
  • Элемент <xsl:key>: выбор по ключу 
  • Элемент <xsl:document>: создание нескольких результирующих документов
  • Элемент <xsl:namespace-alias>: генерация таблиц стилей
  • Глава 9

    Именованные шаблоны, параметры и переменные

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

    Если задать шаблону имя, его можно вызывать по этому имени при помощи элемента

    <xsl:call-template>
    . Шаблон применяется при вызове, поэтому вместо того чтобы полагаться на обработку таблицы стилей, принятую в процессоре XSLT по умолчанию, вы можете явно задать, когда и какой шаблон нужно применять. Например, несколько шаблонов могут выбирать один и тот же набор узлов, и вам нужно выбрать из них один или несколько шаблонов, которые будут применены. Именованные шаблоны похожи на режимы, но дают вам больше возможностей управления.

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

    language
    (язык). При вызове именованного шаблона, заданного для обработки этого параметра, вы можете установить язык в «
    en
    », «
    de
    » или «
    fr
    » и затем вызвать шаблон при помощи элемента
    <xsl:with-param>
    . В самом именованном шаблоне используемый им параметр
    language
    объявляется при помощи элемента
    <xsl:param>
    . После объявления параметра к его значению можно свободно обращаться как
    $language
    и использовать его в выражениях XPath. В этой главе мы рассмотрим многочисленные примеры работы с параметрами.

    Переменные во многом похожи на параметры, с тем лишь отличием, что они по-другому создаются. Параметры, как правило, используются в именованных шаблонах, в то время как переменные применяются более широко, в выражениях XPath любого вида. Как и в языках программирования, в переменных XSLT можно хранить значения и обращаться к ним позже. Но есть одна важная особенность: за исключением особых обстоятельств, вы не можете изменять значение, хранимое в переменной. (В связи с этим некоторые авторы, пишущие об XSLT, считают неправильным называть их переменными.)

    Переменные удобны для хранения значений, создание которых занимает длительное время, но в таблице стилей они часто используются. Вместо того, чтобы каждый раз заново создавать эти значения, сохраните их в переменной и ссылайтесь на ее значение. Как и в случае с параметрами, для получения значения переменной добавьте префикс «$». Например, для переменной с именем

    sandwich
    получить ее значение можно при помощи
    $sandwich
    . Как и в параметрах, в переменных можно хранить данные всех четырех типов данных XPath. Переменные также имеет смысл применять для хранения значений, которые позже в шаблоне будут изменены. Например, «.» обычно ссылается на контекстный узел шаблона, но внутри элемента
    <xsl:for-each>
    «.» ссылается на текущий обрабатываемый в элементе узел, а не на контекстный узел всего шаблона. Для того чтобы обратиться к контекстному узлу, перед входом в цикл
    <xsl:for-each>
    сохраните его в переменной
    contextnode
    и затем в теле цикла используйте это значение как
    $contextnode
    .

    Кроме четырех типов данных XPath мы также будем использовать тип данных, поддерживаемый в XSLT 1.0, но не в XSLT 1.1 — фрагменты результирующего дерева, которые создаются элементами

    <xsl:variable>
    или
    <xsl:with-param>
    . Фрагменты результирующего дерева могут быть удобны в определенных случаях, как вы увидите далее в этой главе.

    Наконец, в этой главе мы также рассмотрим элемент

    <xsl:key>
    . Впервые он нам встретился в главе 4, теперь мы изучим его более подробно.

    Для введения вполне достаточно; давайте перейдем к работе, и начнем мы с переменных.

    Элемент <xsl:variable>: создание переменных

    Для создания переменных в XSLT служит элемент

    <xsl:variable>
    , обладающий следующими атрибутами:

    • 

    name
    (обязательный). Имя переменной, устанавливается в QName;

    • 

    select
    (необязательный). Выражение XPath, задающее значение переменной. Если опустить этот атрибут, значение переменной будет определяться содержимым
    <xsl:variable>
    .

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

    select
    .

    Для создания переменной присвойте ее имя атрибуту name элемента

    <xsl:variable>
    , а значение переменной атрибуту
    select
    , как в следующем примере, в котором я создаю переменную
    number_books
    и сохраняю в ней значение 255: 

    <xsl:variable name="number_books" select="255"/>

    .

    .

    .

    Получить значение переменной можно, добавив к ее имени префикс $:

    <xsl:variable name="number=books" select="255"/>

    <xsl:text>There are </xsl:text>

    <xsl:value-of select="$number books"/>

    <xsl:text> books in my library </xsl:text>

    Заметьте, что если вы присваиваете переменной литерал — как, например, присваивание значения «turkey» (индейка) переменной

    sandwich
    (бутерброд), — литерал необходимо заключить в кавычки, причем они должны отличаться от кавычек, в которые заключены значения атрибутов:

    <xsl:variable name="sandwich" select="'turkey'"/>

    В XSLT 1.0 нет необходимости в атрибуте select — данные можно заключить внутри самого элемента

    <xsl:variable>
    :

    <xsl:variable name="sandwich">turkey</xsl:variable>

    Формально, однако, при пропуске атрибута

    select
    в элементах
    <xsl:variable>
    или
    <xsl:with-param>
    и задании этим элементам содержимого вы создаете фрагмент результирующего дерева, который больше не допускается в XSLT 1.1.

    Стоит отметить, что имя переменной может включать префикс, как, например,

    star:PLANET
    , который должен соответствовать активному пространству имен. Сравнения осуществляются не сравнением префиксов, а проверкой фактического URI префикса — поэтому
    star:PLANET
    может быть тем же самым, что и
    nebula:PLANET
    , если пространства имен
    star
    и
    nebula
    соответствуют одному и тому же URI.

    Область видимости переменной

    Элемент

    <xsl:variable>
    можно использовать как элемент верхнего уровня или внутри тела шаблона для создания переменных. Переменные, созданные в элементах
    <xsl:variable>
    высокого уровня, обладают глобальной областью видимости, созданные в телах шаблона — локальной. Область видимости переменной определяет, в какой части таблицы стилей вы можете ее использовать.

    Областью видимости глобальной переменной является вся таблица стилей, подразумевая и импортированные или включенные таблицы стилей. Это означает, что переменная доступна в любом месте таблицы стилей, если только она не будет перекрыта локальной переменной с тем же именем. Можно даже обращаться к глобальной переменной до ее объявления. Однако нельзя создавать циклические ссылки (то есть если вы объявили

    a
    через
    b
    , нельзя объявлять
    b
    через
    а
    ).

    Область видимости локальной переменной ограничена следующими за ней братьями или потомками последующих братьев. В частности это значит, что если вы объявили переменную внутри таких элементов, как

    <xsl:choose>
    ,
    <xsl:if>
    или
    <xsl:for-each>
    , она не будет доступна вне этих элементов.

    Как правило, вы не можете изменять значение переменной, но вы можете перекрыть ее локальной переменной. То есть локальные переменные перекрывают глобальные в пределах области видимости локальных переменных. Пусть, например, я объявил переменную с именем

    movie
    (кинокартина):

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"/>

    <!-- здесь $movie = 'Mr. Blandings Builds His Dream House" -->

    .

    .

    .

    Это элемент верхнего уровня, поэтому

    movie
    — глобальная переменная. Даже внутри шаблонов movie будет сохранять свое начальное значение, если не будет локальной переменной с таким же именем:

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"/>

    <!-- здесь $movie = "Mr Blandings Builds His Dream House'-->

    <xsl:template match="entertainment">

     <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

     .

     .

     .

    Однако если вы объявите локальную переменную movie, в шаблоне эта версия перекроет глобальную переменную:

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'">

    <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

    <xsl:template match="entertainment">

     <!-- здесь $movie = Mr. Blandings Builds His Dream House"-->

     <xsl:variable name="movie" select="'Goldfinger'"/>

     <!-- здесь $movie = 'Goldfinger'-->

     .

     .

     .

    В этом случае мы перекрыли глобальную переменную при помощи глобальной. Заметьте, однако, что нельзя снова объявить одну и ту же переменную в одном шаблоне с целью попытаться изменить ее значение:

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"/>

    <!-- здесь $movie = Mr. Blandings Builds His Dream House"-->

    <xsl:template match="entertainment">

     <!-- здесь $movie = 'Mr. Blandings Builds His Dream House"-->

     <xsl:variable name="movie" select="'Goldfinger'"/>

     <!-- здесь $movie = 'Goldfinger'-->

     <xsl:variable name="movie" select="'Withnail and I'"/><!-- Запрещено -->

     .

     .

     .

    За пределами шаблона локальная переменная невидима, и

    movie
    содержит глобальное значение:

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"/>

    <!-- здесь $movie = 'Mr Bindings Builds His Dream House' -->

    <xsl:template match="entertainment">

     <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

     <xsl:variable name="movie" select="'Goldfinger'"/>

     <!-- здесь $movie = 'Goldfinger'-->

     <xsl:variable name="movie" select="'Withnail and I'"/><!-- Запрещено -->

    </xsl:template>

    <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

    .

    .

    .

    Глобальные переменные тоже нельзя объявлять повторно:

    <xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"/>

    <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

    <xsl:template match="entertainment">

     <!-- здесь $movie = 'Mr. Blandings Builds His Dream House"-->

     <xsl:variable name="movie" select="'Goldfinger'"/>

     <!-- здесь $movie = 'Goldfinger'-->

     <xsl:variable name="movie" select="'Withnail and I'"/><!-- Запрещено -->

    </xsl:template>

    <!-- здесь $movie = 'Mr. Blandings Builds His Dream House'-->

    <xsl:variable name="movie" select="'Goldfinger'"/><!-- Запрещено -->
     

    Несмотря на все эти ограничения, вы можете менять значение переменной на каждом шаге цикла

    <xsl:for-each>
    , как мы увидим в следующем разделе.

    Работа с переменными

    Давайте рассмотрим примеры применения переменных. В следующем примере (листинг 9.1) я присваиваю переменной

    copyright
    сообщение об авторских правах и затем с ее помощью добавляю атрибут
    copyright
    во все элементы
    planets.xml
    .

    Листинг 9.1. Применение переменной

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output method="xml"/>

     <xsl:variable name="copyright" select="'(c)2002 Starpowder Inc.'"/>

     <xsl:template match="*">

      <xsl:copy>

       <xsl:attribute name="copyright">

        <xsl:value-of select="$copyright"/>

       </xsl:attribute>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Вот результирующий документ, дополненный атрибутами copyright

    <?xml version="1.0" encoding="utf-8"?>

    <PLANETS copyright="(c)2002 Starpowder Inc.">

     <PLANET copyright="(c)2002 Starpowder Inc.">

      <NAME copyright="(c)2002 Starpowder Inc.">Mercury</NAME>

      <MASS copyright="(c)2002 Starpowder Inc.">.0553</MASS>

      <DAY copyright="(с)2002 Starpowder Inc.">58.65</DAY>

      <RADIUS copyright="(c)2002 Starpowder Inc.">1516</RADIUS>

      <DENSITY copyright="(c)2002 Starpowder Inc.">.983</DENSITY>

      <DISTANCE copyright="(с)2002 Starpowder Inc.">43.4</DISTANCE>

     </PLANET>

     <PLANET copyright="(c)2002 Starpowder Inc.">

      <NAME copyright="(c)2002 Starpowder Inc.">Venus</NAME>

      <MASS copyright="(c)2002 Starpowder Inc.">.815</MASS>

      <DAY copyright="(с)2002 Starpowder Inc.">116.75</DAY>

      <RADIUS copyright="(c)2002 Starpowder Inc.">3716</RADIUS>

      <DENSITY copyright="(c)2002 Starpowder Inc.">.943</DENSITY>

      <DISTANCE copyright="(c)2002 Starpowder Inc.">66.8</DISTANCE>

     </PLANET>

     .

     .

     .

    Переменные зачастую удобны для хранения значении, зависимых от контекста, и мы сейчас рассмотрим еще один пример, о котором я упоминал в начале главы. В этом случае я преобразую

    planets.xml
    в новый документ, в котором для каждой планеты будет один элемент. Каждый из этих новых элементов будет содержать два элемента
    <SIBLINGPLANET>
    , содержащих планеты-братья текущей планеты — например, братьями Земли будут Венера и Меркурий:

    <?xml version="1.0" encoding="utf-8"?>

    <Mercury>

     <SIBLINGPLANET>

      Venus

     </SIBLINGPLANET>

     <SIBLINGPLANET>

      Earth

     </SIBLINGPLANET>

    </Mercury>

    <Venus>

     <SIBLINGPLANET>

      Mercury

     </SIBLINGPLANET>

     <SIBLINGPLANET>

      Earth

     </SIBLINGPLANET>

    </Venus>

    <Earth>

     <SIBLINGPLANET>

      Mercury

     </SIBLINGPLANET>

     <SIBLINGPLANET>

      Venus

     </SIBLINGPLANET>

    </Earth>

    Для примера я поочередно выбираю каждый элемент

    <PLANET>
    и прохожу в цикле
    <xsl:for-each>
    по всем планетам, создавая элементы
    <SIBLINGPLANET>
    для всех планет, не являющихся контекстным узлом. Однако откуда мне известно внутри элемента
    <xsl:for-each>
    , какая из планет является контекстным узлом, выбранным шаблоном? Внутри элемента
    <xsl:for-each>
    «.» ссылается на текущий узел, с которым работает
    <xsl:for-each>
    , но не на контекстный узел шаблона. Проблему можно решить, если сохранить контекстный узел в переменной, которую я назвал
    contextnode
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <xsl:for-each select="PLANET">

       <xsl:element name="{NAME}">

        <xsl:variable name="contextnode" select="."/>

        .

        .

        .

    Теперь для проверки в цикле

    <xsl:for-each>
    того, что текущий элемент не является контекстным узлом, я могу обратиться к контекстному узлу шаблона как
    $contextnode
    (листинг 9.2).

    Листинг 9.2. Хранение в переменной информации, зависимой от контекста

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <xsl:for-each select="PLANET">

       <xsl:element name="{NAME}">

        <xsl:variable name="contextnode" select="."/>

        <xsl:for-each select="//PLANET">

         <xsl:if test=". != $contextnode">

          <xsl:element name="SIBLINGPLANET">

           <xsl:value-of select="NAME"/>

          </xsl:element>

         </xsl:if>

        </xsl:for-each>

       </xsl:element>

      </xsl:for-each>

     </xsl:template>

    </xsl:stylesheet>

    Теперь наша проблема решена.

    Если у элемента

    <xsl:variable>
    есть тело, он создает переменную, чье значение является фрагментом результирующего дерева. В следующем примере при помощи фрагмента результирующего дерева я задаю значение по умолчанию для атрибута
    COLOR
    (цвет), если значение для него уже не задано. Значение по умолчанию я устанавливаю в «
    blue
    » (голубой):

    <xsl:variable name="COLOR">

     <xsl:choose>

      <xsl:when test="@COLOR">

       <xsl:value-of select="@COLOR"/>

      </xsl:when>

      <xsl:otherwise>blue</xsl:otherwise>

     </xsl:choose>

    </xsl:variable>

    Строковое значение фрагмента результирующего дерева (то есть либо значение атрибута

    COLOR
    , либо значение по умолчанию, «
    blue
    ») присваивается переменной
    COLOR
    . Теперь в выражениях XPath можно обращаться к значению этой переменной,
    $COLOR
    , а не к значению атрибута (
    @COLOR
    , гарантированно получая при этом значение цвета, даже если у соответствующего элемента отсутствует атрибут
    COLOR
    .

    Вот еще один пример фрагмента результирующего дерева. В этом случае я сохраняю элемент буквального результата в переменной

    START_HTML
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output method="html"/>

     <xsl:variable name="START_HTML">

      <HEAD>

       <TITLE>

        My page

       </TITLE>

      </HEAD>

     </xsl:variable>

     .

     .

     .

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

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:output method="html"/>

     <xsl:variable name="START_HTML">

      <HEAD>

       <TITLE>

        My page

       </TITLE>

      </HEAD>

     </xsl:variable>


     <xsl:template match="PLANETS">

      <HTML>

       <xsl:copy-of select="$START HTML"/>

       <BODY>

        <H1>Welcome to my page</H1>

       </BODY>

      </HTML>

     </xsl:template>

    </xsl:stylesheet>

    И вот результат:

    <HTML>

     <HEAD>

      <TITLE>

       My page

      </TITLE>

     </HEAD>

     <BODY>

      <H1>Welcome to my page</H1>

     </BODY>

    </HTML>

    Однако поскольку теперь фрагменты результирующего дерева не допускаются в XSLT 1.1, этот пример работать не будет. Как же тогда сохранить весь элемент буквального результата одновременно с возможностью простого вызова? Вы можете создать именованный шаблон.

    Элемент <xsl:call-template>: применение именованных шаблонов

    У элемента

    <xsl:template>
    есть атрибут
    name
    , задающий имя шаблона. Предположим, у меня есть элемент буквального результата, состоящий из двух элементов
    <BR>
    и двух элементов
    <HR>
    HTML, при помощи которого я создаю в документах HTML вертикальный разделитель:

    <BR/>

    <HR/>

    <BR/>

    <HR/>

    Тогда я могу создать шаблон с именем «separator» (разделитель), использующий этот элемент буквального результата:

    <xsl:template name="separator">

     <BR/>

     <HR/>

     <BR/>

     <HR/>

    </xsl:template>

    Это именованный шаблон — для его создания нужно только присвоить имя атрибуту name элемента

    <xsl:template>
    .

    Заметьте, что этот шаблон не установлен для выбора чего-то конкретного. Для активизации шаблона необходимо вызвать его явно. Для этого служит элемент

    <xsl:call-template>
    , обладающий только одним атрибутом:

    • 

    name
    (обязательный). Имя вызываемого шаблона, устанавливается в
    QName
    .

    Следующий пример демонстрирует применение нашего шаблона «separator», для чего мне нужно было только вызвать его в соответствующих местах:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         The Planets Table </TITLE>

        </HEAD>

        <BODY>

         <H1>

          The Planets Table

         </Н1>

         <xsl:call-template name="separator"/>

         <TABLE BORDER="2">

          <TR>

           <TD>Name</TD>

           <TD>Mass</TD>

           <TD>Radius</TD>

           <TD>Day</TD>

          </TR>

          <xsl:apply-templates/>

         </TABLE>

         <xsl:call-template name="separator"/>

        </BODY>

       </HTML>

      </xsl:template>


      <xsl:template match="PLANET">

       <TR>

        <TD><xsl:value-of select="NAME"/></TD>

        <TD><xsl:apply-templates select="MASS"/></TD>

        <TD><xsl:apply-templates select="RADIUS"/></TD>

        <TD><xsl:apply-templates select="DAY"/></TD>

       </TR>

     </xsl:template>


     <xsl:template match="MASS">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>


     <xsl:template match="RADIUS">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>


     <xsl:template match="DAY">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>


     <xsl:template name="separator">

      <BR/>

      <HR/>

      <BR/>

      <HR/>

     </xsl:template>

    </xsl:stylesheet>

    Вот результат. Обратите внимание: элементы

    <BR>
    и
    <HR>
    были вставлены так, как требовалось:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <BR>

      <HR>

      <BR>

      <HR>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553 (Earth = 1)</TD>

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815 (Earth = 1)</TD>

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

        <TD>1 (Earth = 1)</TD>

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

      <BR>

      <HR>

      <BR>

      <HR>

     </BODY>

    </HTML>

    Результирующий документ показан на рис. 9.1.

    Рис. 9.1. Вызов именованного шаблона


    Таким образом, вы можете обращаться к элементу буквального результата по имени, легко помещая его в результирующий документ. Но это слишком статично — элемент буквального результата всегда один и тот же. Впрочем, как можно заметить, вызов именованного шаблона во многом похож на вызов подпрограммы в языке программирования. Так же, как вы передаете данные в подпрограмму, вы можете передать данные в именованные шаблоны при помощи параметров.

    Элементы <xsl:param> и <xsl:with-param>: создание параметров

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

    <xsl:param>
    , обладающим двумя атрибутами:

    • 

    name
    (обязательный). Имя переменной, устанавливается в QName;

    • 

    select
    (необязательный). Значение параметра по умолчанию. Устанавливается в выражение XPath.

    Аналогично

    <xsl:variable>
    , этот элемент может либо быть элементом высокого уровня, либо применяться внутри тела шаблона. Параметры, созданные элементами
    <xsl:param>
    , являются глобальными, а созданные внутри шаблонов — локальными. При создании параметра внутри тела шаблона, элемент
    <xsl:param>
    должен быть расположен перед какими-либо другими дочерними элементами. Элемент может также содержать необязательное тело шаблона, создающее фрагмент результирующего дерева — в XSLT 1.1 это уже не разрешено. Если элемент содержит тело, атрибут
    select
    использовать нельзя.

    После объявления параметра при помощи

    <xsl:param>
    к его значению можно обращаться в шаблоне таким же способом, как и к значению переменной, — добавляя к имени префикс «$».

    Когда вы вызываете именованный шаблон при помощи

    <xsl:call-template>
    или применяете шаблоны с использованием
    <xsl:apply-templates>
    , вы можете задать значение используемых в шаблоне параметров с помощью элемента
    <xsl:with-param>
    . Если самому параметру при объявлении было присвоено значение атрибутом
    select
    элемента
    <xsl:param>
    , это значение выступает в качестве значения параметра по умолчанию. Значение по умолчанию будет перекрыто, если вы зададите новое значение параметра при помощи элемента
    <xsl:with-param>
    . У элемента
    <xsl:with-param>
    два атрибута:

    • 

    name
    (обязательный). Имя переменной; устанавливается в QName;

    • 

    select
    (необязательный). Выражение XPath, задающее значение параметра. Если опустить этот атрибут, значение переменной будет определяться содержимым
    <xsl:with-param>
    .

    Элемент может также содержать необязательное тело шаблона, создающее фрагмент результирующего дерева, но в XSLT 1.1 это уже не разрешено.

    В следующем примере (листинг 9.3) я создаю именованный шаблон с именем «COLORS» (цвета), добавляющий цвет в данные планет в результирующем HTML-документе. Этот именованный шаблон использует единственный параметр,

    COLOR
    , который устанавливается в требуемый цвет. В примере я устанавливаю параметр
    COLOR
    в разные цвета для разных планет, используя
    <xsl:with-param>
    и вызывая шаблон «COLORS». Вот как это реализуется на практике.

    Листинг 9.3. Применение параметров таблиц стилей

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/PLANETS">

      <HTML>

      .

      .

      .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME='Mercury'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'RED'"/>

       </xsl:call-template>

      </xsl:if>

      <xsl:if test="NAME='Venus'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'GREEN'"/>

       </xsl:call-template>

      </xsl:if>

      <xsl:if test="NAME='Earth'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'BLUE'"/>

       </xsl:call-template>

      </xsl:if>

     </xsl:template>

     .

     .

     .

    Данная таблица вызывает шаблон «COLORS» с разными значениями параметра COLOR. Я могу воспользоваться этими цветами при форматировании данных планет. Заметьте, что я объявил параметр COLOR при помощи

    <xsl:param>
    в самом начале шаблона «COLORS»:

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME='Mercury'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'RED'"/>

       </xsl:call-template>

      </xsl:if>

      <xsl:if test="NAME='Venus'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'GREEN'"/>

       </xsl:call-template>

      </xsl:if>

      <xsl:if test="NAME='Earth'">

       <xsl:call-template name="COLORS">

        <xsl:with-param name="COLOR" select="'BLUE'"/>

       </xsl:call-template>

      </xsl:if>

     </xsl:template>


     <xsl:template name="COLORS"> <xsl:param name="COLOR"/>

      <TR>

       <TD>

        <FONT COLOR="{$COLOR}"><xsl:value-of select="NAME"/></FONT>

       </TD>

       <TD>

        <FONT COLOR="{$COLOR}"><xsl:apply-templates select="MASS"/></FONT>

       </TD>

       <TD>

        <FONT COLOR="{$COLOR}"><xsl:apply-templates select="RADIUS"/></FONT>

       </TD>

       <TD>

        <FONT COLOR="{$COLOR}"><xsl:apply-templates select="DAY"/></FONT>

       </TD>

      </TR>

     </xsl:template>


     <xsl:template match="MASS">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>

     .

     .

     .

     <xsl:template match="DAY">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>

    </xsl:stylesheet>

    И вот результат:

    <HTML>

     <HEAD>

      <TITLE>

       The Colorful Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Colorful Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD><FONT COLOR="RED">Mercury</FONT></TD>

        <TD><FONT COLOR="RED">.0553 (Earth = 1)</FONT></TD>

        <TD><FONT COLOR="RED">1516 miles</FONT></TD>

        <TD><FONT COLOR="RED">58.65 days</FONT></TD>

       </TR>

       <TR>

        <TD><FONT COLOR="GREEN">Venus</FONT></TD>

        <TD><FONT COLOR="GREEN">.815 (Earth = 1)</FONT></TD>

        <TD><FONT COLOR="GREEN">3716 miles</FONT></TD>

        <TD><FONT COLOR="GREEN">116.75 days</FONT></TD>

       </TR>

       <TR>

        <TD><FONT COLOR="BLUE">Earth</FONT></TD>

        <TD><FONT COLOR="BLUE">1 (Earth = 1)</FONT></TD>

        <TD><FONT COLOR="BLUE">2107 miles</FONT></TD>

        <TD><FONT COLOR="BLUE">1 days</FONT></TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Этот результирующий документ показан на рис. 9.2 (пусть даже в черно-белом исполнении).

    Рис. 9.2. Вызов именованного шаблона с параметрами


    В следующем примере я использую параметры для локализации используемого в шаблоне языка. Я создаю новый шаблон

    localize
    с параметром
    language
    . Если language установлен в «
    en
    », означая английский, результирующий документ будет озаглавлен «Planets»; если
    language
    установлен в «
    de
    », немецкий, результирующий документ будет озаглавлен «Planeten», а если в «
    fr
    », французский, результирующий документ будет озаглавлен «Planetes».

    Здесь я вызываю шаблон

    localize
    , установив
    language
    в «fr»:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         <xsl:call-template name="localize">

          <xsl:with-param name="language" select="'fr'"/>

         </xsl:call-template>

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         <xsl:call-template name="localize">

          <xsl:with-param name="language" select="'fr'"/>

         </xsl:call-template>

         .

         .

         .

    А вот как выглядит параметризованный шаблон «COLORS». Заметьте, что я объявляю параметр

    COLOR
    в шаблоне при помощи элемента
    <xsl:param>
    (и заметьте, что в HTML 4.01 символьный элемент для «е» в «Planetes», который я и использую — это
    &#232;
    ):

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         <xsl:call-template name="localize">

          <xsl:with-param name="language" select="'fr'"/>

         </xsl:call-template>

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         <xsl:call-template name="localize">

          <xsl:with-param name="language" select="'fr'"/>

         </xsl:call-template>

        </H1>

        <TABLE BORDER="2">

         <TR>

          <TD>Name</TD>

          <TD>Mass</TD>

          <TD>Radius</TD>

          <TD>Day</TD>

         </TR>

         <xsl:apply-templates/>

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template name="localize">

      <xsl:param name="language"/>

      <xsl:if test="$language='en'">

       <xsl:text>Planets</xsl:text>

      </xsl:if>

      <xsl:if test="$language='de'">

       <xsl:text>Planeten</xsl:text>

      </xsl:if>

      <xsl:if test="$language='fr'">

       <xsl:text>Plan&#232;tes</xsl:text>

      </xsl:if>

     </xsl:template>

     .

     .

     .

    Вот результирующий локализованный документ:

    <HTML>

     <HEAD>

      <TITLE>Plan&egrave;tes</TITLE>

     </HEAD>

     <BODY>

      <H1>Plan&egrave;tes</H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       .

       .

       .

    Этот документ показан на рис. 9.3.

    Рис. 9.3. Вызов именованного шаблона с параметрами для установки языков


    Вызов шаблона во многом похож на вызов функции, что обсуждалось в главе 8, и способность передавать данные через параметры усиливает сходство. Но в отсутствие оператора присваивания, как в языках программирования, кажется, что нельзя присвоить переменной значение, возвращаемое именованным шаблоном, впрочем, поразмыслив, это можно сделать. С шаблонами можно и еще в одном случае поступить так же, как с функциями, — осуществить рекурсивный вызов.

    Рекурсивный вызов шаблонов

    Эта тема предназначена, главным образом, для программистов, поскольку здесь я буду пользоваться XSLT как языком программирования. В частности, я реализую вызов именованным шаблоном самого себя, то есть рекурсивный вызов. Классический пример рекурсии — вычисление факториала: например, факториал 6, что записывается как

    6!
    , равен
    6*5*4*3*2*1
    , или
    720
    .

    При реализации рекурсии в настоящем языке программирования создается функция — например,

    factorial
    , которая вызывается со значением 6:
    factorial(6)
    . Факториал 6 вычисляется как
    6 * factorial(5)
    , поэтому функции нужно лишь умножить на 6 результат вызова самой себя со значением 5, то есть
    factorial(5)

    Далее,

    factorial(5)
    — это
    5*factorial(4)
    , поэтому функция снова вызывает сама себя, чтобы вычислить значение
    factorial(4)
    . Этот процесс продолжается до вычисления
    factorial(1)
    , а мы знаем, что 1! — это просто 1, поэтому
    factorial(1)
    возвращает 1. С этого момента управление последовательно возвращается на все предыдущие этапы, в результате чего будет вычислено выражение
    1*2*3*4*5*6
    , или
    720
    , что составляет
    6!
    .

    Кажется, что в таком языке стилей, как XSLT, реализовать подобное невозможно. Тем не менее, это можно сделать, по крайней мере, в XSLT 1.0. Основная идея состоит в том, что значение, возвращаемое шаблоном, можно сохранять в переменной, если шаблон вызывается внутри элемента

    <xsl:variable>
    , в котором объявляется эта переменная. Пусть, например, у нас есть именованный шаблон
    factorial
    , и мы хотим вычислить
    6!
    . Тогда шаблону можно передать значение 6 при помощи элемента
    <xsl:with-param>
    и присвоить строковое значение результата переменной
    result
    , которое я затем показываю:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/">

      <xsl:variable name="result">

       <xsl:call-template name="factorial">

        <xsl:with-param name="value" select="6"/>

       </xsl:call-template>

      </xsl:variable>

      6! = <xsl:value-of select="$result"/>

     </xsl:template>

     .

     .

     .

    Следующий пример демонстрирует, как можно реализовать шаблон

    factorial
    , чтобы для вычисления факториала он вызывал сам себя. На языке программирования я мог бы написать рекурсивный вызов как
    n!=n*factorial(n-1)
    , но у нас нет оператора присваивания; поэтому, когда я вычисляю
    factorial(n-1)
    , я сохраняю это значение в новой переменной
    temp
    и на каждом шаге возвращаю значение
    n*$temp
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template match="/">

      <xsl:variable name="result">

       <xsl:call-template name="factorial">

        <xsl:with-param name="value" select="6"/>

       </xsl:call-template>

      </xsl:variable>

      6! = <xsl:value-of select="$result"/>

     </xsl:template>


     <xsl:template name="factorial">

      <xsl:param name="value"/>

      <xsl:choose>

       <xsl:when test="$value=1">

        <xsl:value-of select="1"/>

       </xsl:when>

       <xsl:otherwise>

        <xsl:variable name="temp">

         <xsl:call-template name="factorial">

          <xsl:with-param name="value" select="$value - 1"/>

         </xsl:call-template>

        </xsl:variable>

        <xsl:value-of select="$temp * $value"/>

       </xsl:otherwise>

      </xsl:choose>

     </xsl:template>

    </xsl:stylesheet>

    Вот результирующий документ:

    <?xml version="1.0" encoding="utf-8"?>

    6! = 720

    Как видите, это можно сделать, по крайней мере, в XSLT 1.0, в котором разрешены использованные здесь фрагменты результирующего дерева.

    Шаблон: значение по умолчанию

    Как я говорил ранее, в случае задания параметру значения при объявлении, оно может быть перекрыто, если вы зададите другое значение в элементе

    <xsl:with-param>
    . Но если другого значения не указывать, исходное значение выступит в роли значения по умолчанию.

    Следующий пример видоизменяет рассмотренный ранее пример «COLORS». Шаблон имеет параметр

    COLOR
    , но я могу вызвать шаблон, не устанавливая
    COLOR
    в какое-либо определенное значение:

    <xsl:template match="PLANET">

     <xsl:if test="NAME='Mercury'">

      <xsl:call-template name="COLORS">

       <xsl:with-param name="COLOR" select="'RED'"/>

      </xsl:call-template>

     </xsl:if>

     <xsl:if test="NAME='Venus'">

      <xsl:call-template name="COLORS">

       <xsl:with-param name="COLOR" select="'GREEN'"/>

      </xsl:call-template>

     </xsl:if>

     <xsl:if test="NAME='Earth'">

      <xsl:call-template name="COLORS">

      </xsl:call-template>

     </xsl:if>

    </xsl:template>

    В этом случае параметр

    COLOR
    принимает значение по умолчанию«
    blue
    » (голубой), заданное в элементе
    <xsl:param>
    в шаблоне «COLORS»:

    <xsl:template match="PLANET">

     <xsl:if test="NAME='Mercury'">

      <xsl:call-template name="COLORS">

       <xsl:with-param name="COLOR" select="'RED'"/>

      </xsl:call-template>

     </xsl:if>

     <xsl:if test="NAME='Venus'">

      <xsl:call-template name="COLORS">

       <xsl:with-param name="COLOR" select="'GREEN'"/>

      </xsl:call-template>

     </xsl:if>

     <xsl:if test="NAME='Earth'">

      <xsl:call-template name="COLORS">

      </xsl:call-template>

     </xsl:if>

    </xsl:template>


    <xsl:template name="COLORS">

     <xsl:param name="COLOR" select="'blue'"/>

     <TR>

      <TD>

       <FONT COLOR="{$COLOR}"><xsl:value-of select="NAME"/></FONT>

      </TD>

      <TD>

       <FONT COLOR="{$COLOR}"><xsl:apply-templates select="MASS"/></FONT>

      </TD>

      <TD>

       <FONT COLOR="{$COLOR}"><xsl:apply-templates select="RADIUS"/></FONT>

      </TD>

      <TD>

       <FONT COLOR="{$COLOR}"><xsl:apply-templates select="DAY"/></FONT>

      </TD>

     </TR>

    </xsl:template>

    Как задавать значения шаблона в командной строке

    Кроме возможностей, предоставляемых элементами

    <xsl:param>
    и
    <xsl:with-param>
    , значение параметров таблицы стилей во многих процессорах XSLT можно также задавать в командной строке. Способ зависит от конкретного процессора.

    Следующий пример показывает, как можно присвоить параметру

    param1
    значение
    value1
    в командной строке, используя процессор XSLT от Oracle в Windows. Заметьте, что при установке значений параметра в командной строке его все равно нужно объявлять в таблице стилей при помощи
    <xsl:param>
    :

    C:\>java oraclе.xml.parser.v2.oraxsl -p param1='value1' planets.xml planets.xsl output.xml

    Вот как то же самое можно сделать при помощи Saxon:

    C:\>saxon source.xml stylesheet.xsl param1=value1 > output.xml

    при помощи Xalan:

    C:\>java org.apache.xalan.xslt.Process -IN planets.xml -XSL planets.xsl -OUT output.xml -PARAM parma1 value1

    и XT:

    C:\XSL>java -Dcom jclark.xsl.sax parser=org.apache.xerces.parsers.SAXParser.com.jclark.xsl.sax.Driver planets.xml planets.xsl output.xml param1=value1

    Элемент <xsl:key>: выбор по ключу 

    Элемент

    <xsl:key>
    позволяет создать ключ и выбрать узлы по этому ключу. Он обладает следующими атрибутами:

    • 

    name
    (обязательный). Имя ключа, устанавливается в
    QName
    ;

    • 

    match
    (обязательный). Устанавливается в образец выбора для выбора узлов, к которым применяется ключ;

    • 

    use
    (обязательный). Устанавливается в выражение, определяющее значение ключа.

    При помощи ключей можно легко распознавать элементы; конкретные ключи можно выбрать при помощи образца «key()». В главе 4 мы познакомились с ключами, имеющими одно значение. В следующем примере я использую ключи для выбора планет, у которых атрибут

    COLOR
    был установлен в «BLUE» — в данном случае это Земля:

    <?xml version="1.0"?>

    <?xml-stylesheet type="text/xml" href="planets.xsl"?>

    <PLANETS>

     .

     .

     .

     <PLANET COLOR="BLUE">

      <NAME>Earth</NAME>

      <MASS UNITS="(Earth = 1)">1</MASS>

      <DAY UNITS="days">1</DAY>

      <RADIUS UNITS="miles">2107</RADIUS>

      <DENSITY UNITS="(Earth = 1)">1</DENSITY>

      <DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии-->

     </PLANET>

    </PLANETS>

    Я создал ключ

    COLOR
    , выбирающий элементы
    <PLANET>
    путем проверки их атрибута
    COLOR
    . Ключ выглядит следующим образом:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:key name="COLOR" match="PLANET" use="@COLOR"/>

     .

     .

     .

    После этого я применил образец «key()» для того, чтобы выбрать элементы

    <PLANET>
    , у которых атрибут
    COLOR
    установлен в «BLUE»:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:key name="COLOR" match="PLANET" use="@COLOR"/>

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         The Planets Table

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         The Planets Table

        </H1>

        <TABLE BORDER="2">

         <TR>

          <TD>Name</TD>

          <TD>Mass</TD>

          <TD>Radius</TD>

          <TD>Day</TD>

         </TR>

         <xsl:apply-templates select="key('COLOR', 'BLUE')"/>

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>

     .

     .

     .

    И вот результат — как видите, образцу удовлетворяет только Земля:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

    <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

        <TD>1 (Earth = 1)</TD>

        <TD>2107 miles</TD>

        <TD>1 days</TD>

    </TR>

      </TABLE>

     </BODY>

    </HTML>

    Но в данном случае существует больше вариантов. Например, ключу могут соответствовать несколько узлов, а это означает, что использующий ключ образец вернет набор узлов. Пусть, например, все планеты будут иметь одно и то же значение атрибута

    COLOR
    , «
    UNKNOWN
    » (неизвестен):

    <?xml version="1.0"?>

    <?xml-stylesheet type="text/xml" href="planets.xsl"?>

    <PLANETS>

     <PLANET COLOR="UNKNOWN">

      <NAME>Mercury</NAME>

      <MASS UNITS="(Earth = 1)">.0553</MASS>

      <DAY UNITS="days">58.65</DAY>

      <RADIUS UNITS="miles">1516</RADIUS>

      <DENSITY UNITS="(Earth = 1)">.983</DENSITY>

      <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии-->

     </PLANET>

     <PLANET COLOR="UNKNOWN">

      <NAME>Venus</NAME>

      <MASS UNITS="(Earth = 1)">.815</MASS>

      <DAY UNITS="days">116.75</DAY>

      <RADIUS UNITS="miles">3716</RADIUS>

      <DENSITY UNITS="(Earth = 1)">.943</DENSITY>

      <DISTANCE UNITS="million miles">66.8</DISTANCE><!--B перигелии-->

     </PLANET>

     <PLANET COLOR="UNKNOWN">

      <NAME>Earth</NAME>

      <MASS UNITS="(Earth = 1)">1</MASS>

      <DAY UNITS="days">1</DAY>

      <RADIUS UNITS="miles">2107</RADIUS>

      <DENSITY UNITS="(Earth = 1)">1</DENSITY>

      <DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии-->

     </PLANET>

    </PLANETS>

    Если создать теперь ключ

    COLOR
    следующим образом:
    <xsl:key name="COLOR" match="PLANET" use="@COLOR"/>
    и осуществить поиск по этому ключу по образцу «
    key('COLOR', 'BLUE')
    », то будут выбраны все три планеты:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553 (Earth = 1)</TD>

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815 (Earth = 1)</TD>

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

        <TD>1 (Earth = 1)</TD>

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Кроме случая, когда ключ выбирает несколько узлов, узел также может предоставить несколько значений для одного ключа. Предположим, вы установили ключ для работы с элементом

    <NAME>
    внутри элемента
    <PLANET>
    . Но что, если каждый элемент
    <PLANET>
    содержит более одного элемента
    <NAME>
    , как в этом примере:

    <?xml version="1.0"?>

    <?xml-stylesheet type="text/xml" href="planets.xsl"?>

    <PLANETS>

     <PLANET>

      <NAME>Mercury</NAME>

      <NAME>Sister Planet</NAME>

      <MASS UNITS="(Earth = 1)">.0553<MASS>

      <DAY UNITS="days">58.65</DAY>

      <RADIUS UNITS="miles">1516</RADIUS>

      <DENSITY UNITS="(Earth = 1)">.983</DENSITY>

      <DISTANCE UNITS="million miles">43.4</DISTANCE><!--B перигелии-->

     </PLANET>

     <PLANET>

      <NAME>Venus</NAME>

      <NAME>Sister Planet</NAME>

      <MASS UNITS="(Earth = 1)">.815</MASS>

      <DAY UNITS="days">116.75</DAY>

      <RADIUS UNITS="miles">3716</RADIUS>

      <DENSITY UNITS="(Earth = 1)">.943</DENSITY>

      <DISTANCE UNITS="million miles">66.8</DISTANCE><!--B перигелии-->

     </PLANET>

     <PLANET>

      <NAME>Earth</NAME>

      <MASS UNITS="(Earth = 1)">1</MASS>

      <DAY UNITS="days">1</DAY>

      <RADIUS UNITS="miles">2107</RADIUS>

      <DENSITY UNITS="(Earth = 1)">1</DENSITY>

      <DISTANCE UNITS="million miles">128.4</DISTANCE><!--B перигелии-->

     </PLANET>

    </PLANETS>

    В этом случае каждый элемент

    <NAME>
    проверяется на совпадение с ключом. Предположим, например, что я хочу выбрать элементы
    <NAME>
    с текстом «Sister Planet». Вот таблица стилей:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:key name="NAME" match="PLANET" use="NAME"/>

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         The Planets Table

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         The Planets Table

        </H1>

        <TABLE BORDER="2">

         <TR>

          <TD>Name</TD>

          <TD>Mass</TD>

          <TD>Radius</TD>

          <TD>Day</TD>

         </TR>

         <xsl:apply-templates select="key('NAME', 'Sister Planet')"/>

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

       <TD><xsl:value-of select="NAME"/></TD>

       <TD><xsl:apply-templates select="MASS"/></TD>

       <TD><xsl:apply-templates select="RADIUS"/></TD>

       <TD><xsl:apply-templates select="DAY"/></TD>

      </TR>

     </xsl:template>


     <xsl:template match="MASS">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>

     .

     .

     .

     <xsl:template match="DAY">

      <xsl:value-of select="."/>

      <xsl:text> </xsl:text>

      <xsl:value-of select="@UNITS"/>

     </xsl:template>

    </xsl:stylesheet>

    И вот результирующий документ:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553 (Earth = 1)</TD>

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815 (Earth = 1)</TD>

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Элемент <xsl:document>: создание нескольких результирующих документов

    Весьма часто во время преобразования требуется создать несколько результирующих документов. Например, вам может понадобиться создать отчет о прохождении преобразования или разделить входной документ на несколько результирующих документов (например, разбить рассказ на главы). Или же вам может быть нужно создать набор результирующих документов, которые будут использоваться (как при создании кадрового (frameset) документа HTML) совместно с двумя документами, отображаемыми во фреймах.

    Создание нескольких результирующих документов настолько распространенная задача, что практически все процессоры XSLT позволяют вам это сделать, даже в XSLT 1.0, где отсутствуют необходимые для этого средства. Процессоры XSLT добавляют для этого новые элементы расширения. Например, Xalan предоставляет элемент

    <write>
    , позволяющий написать новый результирующий документ. Для работы с этим элементом нужно создать новый префикс пространства имен — здесь я использую «xalan» — которое Xalan использует для элемента, «com.lotus.xsl.extensions.Redirect», и указать, что этот новый префикс является префиксом элемента расширения:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xalan="com.lotus.xsl.extensions.Redirect"

     extension-element-prefixes="xalan">

     .

     .

     .

    Теперь при помощи атрибута

    file
    элемента
    <xalan:write>
    можно записать новый файл:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xalan="com.lotus.xsl.extensions.Redirect"

     extension-element-prefixes="xalan">

     .

     .

     .

     <xalan:write file="newdoc.txt">

      <xsl:text>Here's some text.</xsl:text>

     </xalan:write>

    В процессоре Saxon применяйте элемент

    <output>
    . Для этого элемента я использую префикс «saxon», который соответствует URI «http://icl.com/saxon»:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:saxon="http://icl.com/saxon"

     extension-element-prefixes="saxon">

     .

     .

     .

     <saxon:output file="newdoc.txt">

      <xsl:text>Here's some text.</xsl:text>

     </saxon:output>

    To же можно сделать и в XT; в этом случае используйте пространство имен <http://www.jclark.com/xt» с элементом

    <document>
    и атрибут
    href
    для задания имени нового файла:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xt="http://www.jclark.com/xt"

     extension-element-prefixes="xt">

     .

     .

     .

     <xt:document href="newdoc.txt">

      <xsl:text>Here's some text.</xsl:text>

     </xt:document>

    Все это создало весьма запутанную ситуацию, поскольку каждый разрабатывал свои собственные решения. По этой причине в XSLT 1.1 для поддержки нескольких результирующих документов был представлен новый элемент,

    <xsl:document>
    , со следующими атрибутами:

    • 

    href
    (обязательный). Показывает, где будет помещен новый документ. Устанавливается в абсолютный или относительный URI, без идентификатора фрагмента;

    • 

    method
    (необязательный). Определяет метод вывода, используемый для создания результирующего документа. Устанавливается в «xml», «html», «text» или QName, не являющееся NCName;

    • 

    version
    (необязательный). Определяет версию выходного документа. Устанавливается в NMTOKEN;

    • 

    encoding
    (необязательный). Устанавливает кодировку выходного документа. Устанавливается в строку;

    • 

    omit-xml-declaration
    (необязательный). Устанавливается в «yes» или «no», чтобы опустить объявление XML или не отпускать;

    • 

    cdata-section-elements
    (необязательный). Задает имена тех элементов, чье содержимое нужно вывести как разделы CDATA. Устанавливается в список QName, разделенных символами-разделителями;

    • 

    doctype-public
    (необязательный). Задает открытый идентификатор, который будет использован в объявлении
    <!DOCTYPE>
    вывода. Устанавливается в строковое значение;

    • 

    doctype-system
    (необязательный). Задает системный идентификатор, который будет использован в объявлении
    <!DOCTYPE>
    вывода. Устанавливается в строковое значение;

    • 

    indent
    (необязательный). Определяет, будет ли выходной документ выровнен для отображения структуры вложенности. Устанавливается в «yes» или «no»;

    • 

    media-type
    (необязательный). Устанавливает тип MIME вывода. Устанавливается в строковое значение;

    • 

    standalone
    (необязательный). Определяет, будет ли отдельное объявление включено в выходные данные, и если да, устанавливает его значение. Устанавливается в «yes» или «no».

    Этот элемент содержит тело шаблона.

    Следующий пример основан на упрощенной таблице стилей. В этом случае я создал в HTML-документе две рамки (frame), а также два HTML-документа для отображения в этих рамках (

    frame1.html
    и
    frame2.html
    ). Первую рамку и ее документ я создал следующим образом:

    <HTML>

     <HEAD>

      <TITLE>

       Two Frames

      </TITLE>

     </HEAD>

     <FRAMESET cols="50%, 50%">

      <FRAME src="frame1.html"/>

      <xsl:document href="frame1.html">

       <HTML>

        <HEAD>

         <TITLE>

          Frame 1

         </TITLE>

        </HEAD>

        <BODY>

         <H1>This is frame 1.</H1>

        </BODY>

       </HTML>

      </xsl:document>

      .

      .

      .

    Затем я могу создать вторую рамку и ее документ:

    <HTML>

     <HEAD>

      <TITLE>

       Two Frames

      </TITLE>

     </HEAD>

     <FRAMESET cols="50%, 50%">

      <FRAME src="frame1.html"/>

      <xsl:document href="frame1.html">

       <HTML>

        <HEAD>

         <TITLE>

          Frame 1

         </TITLE>

        </HEAD>

        <BODY>

         <H1>This is frame 1.</H1>

        </BODY>

       </HTML>

      </xsl:document>

      <FRAME src=" frame2.html"/>

      <xsl:document href="frame2.html">

       <HTML>

        <HEAD>

         <TITLE>

          Frame 2

         </TITLE>

        </HEAD>

        <BODY>

         <H1>This is frame 2.</H1>

        </BODY>

       </HTML>

      </xsl:document>

     </FRAMESET>

    </HTML>

    Заметьте, однако, что этот пример работоспособен только в XSLT 1.1.

    На момент написания книги в одном процессоре XSLT элемент

    <xsl:document>
    был реализован: это Saxon версии 6.2.1 и старше, в котором свой элемент
    <saxon:output>
    был изменен на
    <xsl:document>
    . Но пока это единственный известный мне процессор XSLT, поддерживающий этот элемент.

    Элемент <xsl:namespace-alias>: генерация таблиц стилей

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

    Но это порождает проблему: если вы обрабатываете таблицу стилей, полную таких элементов, как

    <xsl:template>
    и
    <xsl:apply-templates>
    , которые вы хотите видеть в результирующем документе — поскольку результирующий документ сам является таблицей стилей — как XSLT сможет отличить эти элементы буквального результата от элементов XSLT, которые он должен обработать?

    Тут на помощь приходит элемент

    <xsl:namespace-alias>
    , поскольку он позволяет использовать новое пространство имен для элементов в исходном документе и преобразовывать это пространство имен обратно в правильное в результирующем документе. У элемента два атрибута:

    • 

    stylesheet-prefix
    (обязательный). Префикс пространства имен, используемый в таблице стилей. Устанавливается в NCName или «#default»;

    • 

    result-prefix
    (обязательный). Префикс, URI которого вы хотите присвоить пространству имен в результирующем документе. Устанавливается в NCName или «#default».

    Следующий пример пояснит сказанное. Представьте, что вам нужно создать такую таблицу стилей:

    <?xml version="1.0" encoding="UTF-8"?>

    <xsl:stylesheet

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

     <xsl:template match="PLANET">

      <TR>

       <TD>

        <xsl:value-of select="NAME"/>

       </TD>

       <TD>

        <xsl:apply-templates select="MASS"/>

       </TD>

       <TD>

        <xsl:apply-templates select="RADIUS"/>

       </TD>

       <TD>

        <xsl:apply-templates select="DAY"/>

       </TD>

      </TR>

     </xsl:template>

    </xsl:stylesheet>

    Заметьте, что здесь много элементов XSLT с префиксом «xsl», — значит, если вы попытаетесь создать их при помощи XSLT-преобразования, процессор XSLT попытается выполнить эти элементы. Чтобы избежать такой ситуации, я задал им новый префикс пространства имен, «xslt». Ниже показано, как это выглядит в таблице стилей, производящей предыдущую таблицу стилей (листинг 9.4). Заметьте, что нижеследующая таблица стилей просто выбирает корневой элемент исходного документа, чтобы она могла начать работать; она не использует исходный документ ни для каких других целей.

    Листинг 9.4. Применение <xsl:namespace-alias>

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xslt="http://xslt">

     <xsl:template match="/">

      <xslt:stylesheet version="1.1">

       <xslt:template match="PLANET">

        <TR>

         <TD><xslt:value-of select="NAME"/></TD>

         <TD><xslt:applу-templates select="MASS"/></TD>

         <TD><xslt:apply-templates select="RADIUS"/></TD>

         <TD><xslt:apply-templates select="DAY"/></TD>

        </TR>

       </xslt:template>

      </xslt:stylesheet>

     </xsl:template>

     .

     .

     .

    </xsl:stylesheet>

    Здесь я использую пространство имен "http://xslt" для префикса "xslt", но я смогу изменить его в выходном документе на правильное пространство имен XSLT, "http://www.w3.org/1999/XSL/Transform", воспользовавшись элементом

    <xsl:namespace-alias>
    :

    <xsl:stylesheet version="1.1"

     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

     xmlns:xslt="http://xslt">

     <xsl:template match="/">

      <xslt:stylesheet version="1.1">

       <xslt:template match="PLANET">

        <TR>

         <TD><xslt:value-of select="NAME"/></TD>

         <TD><xslt:applу-templates select="MASS"/></TD>

         <TD><xslt:apply-templates select="RADIUS"/></TD>

         <TD><xslt:apply-templates select="DAY"/></TD>

        </TR>

       </xslt:template>

      </xslt:stylesheet>

     </xsl:template>

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

    </xsl:stylesheet>

    Вот результат. Заметьте, что здесь все еще используется префикс пространства имен «

    xslt
    », но это пространство имен теперь соответствует правильному пространству имен XSLT:

    <?xml version="1.0" encoding="UTF-8"?>

    <xslt:stylesheet

     xmlns:xslt="http://www.w3.org/1999/XSL/Transform" version="1.1">

     <xslt:template match="PLANET">

      <TR>

       <TD>

        <xslt:value-of select="NAME"/>

       </TD>

       <TD>

        <xslt:apply-templates select="MASS"/>

       </TD>

       <TD>

        <xslt:applу-templates select="RADIUS"/>

       </TD>

       <TD>

        <xslt:apply-templates select="DAY"/>

       </TD>

      </TR>

     </xslt:template>

    </xslt:stylesheet>

    На этом глава завершается. В следующей главе мы рассмотрим, как работать с XSLT в коде.







     


    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх