• Элемент <xsl:if>
  • Элементы <xsl:choose>, <xsl:when> и <xsl:otherwise>
  • Элемент <xsl:for-each>
  • Сортирующие элементы
  • Сортировка по нескольким критериям
  • Элемент <xsl:number>
  • Одноуровневая нумерация
  • Нумерация на произвольных уровнях
  • Многоуровневая нумерация
  • Расширяемость XSLT
  • Функции расширения
  • Элемент <xsl:script>
  • Применение функции function-available
  • Внешние объекты
  • Элементы расширения
  • Применение функции element-available
  • Элемент <xsl:fallback>
  • Глава 5

    Принятие решений и сортировка данных

    Эта глава посвящена принятию решений, сортировке и различным способам обработки данных в ХМL-документах. Мы рассмотрим элементы

    <xsl:if>
    ,
    <xsl:choose>
    ,
    <xsl:when>
    ,
    <xsl:otherwise>
    ,
    <xsl:for-each>
    и
    <xsl:sort>
    , при помощи которых вы сможете обрабатывать данные и управлять выполнением преобразования в зависимости от значения данных.

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

    <xsl:script>
    . Этот элемент был предназначен для упрощения применения Java и JavaScript с процессором XSLT. (Для чтения этой книги нет необходимости владеть Java или JavaScript, но если вы знаете эти языки, вам будет приятно удостовериться, что некоторые процессоры XSLT дают возможность использовать их при преобразованиях XML.) Нечто похожее на этот элемент обязательно появится в XSLT 2.0. При помощи расширений вы можете расширять спецификацию XSLT, добавляя в XSLT новые элементы и функции какого-либо производителя или свои собственные.

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

    <xsl:if>
    .

    Элемент <xsl:if>

    При помощи элемента

    <xsl:if>
    осуществляются проверки условия и принимаются действия на основе результата проверки. Он во многом похож на оператор
    if
    в языках программирования. У элемента
    <xsl:if>
    один атрибут:

    • 

    test
    (обязательный). Устанавливается в значение логического (
    Boolean
    ,
    true/false
    ) условия, которое вы хотите проверить.

    Элемент заключает в себе тело шаблона.

    Вот как это работает: вы включаете тело шаблона внутрь элемента

    <xsl:if>
    , проверяющего какое-то выражение. Если это выражение истинно, тело шаблона используется, если ложно — игнорируется:

    <xsl:if test="expression">

     <!--template body-->

    </xsl:if>

    Можно проверять любое выражение XPath. Для преобразования его в значения

    true/false
    в элементе
    <xsl:if>
    применяйте следующие правила:

    • если выражение вычисляется в набор узлов, оно трактуется как

    true
    , когда набор узлов содержит хотя бы один узел;

    • выражение-строка считается

    true
    , если строка не пуста;

    • фрагмент результирующего дерева трактуется как

    true
    , если содержит узлы;

    • если результат выражения — число, он считается

    true
    , когда отличен от нуля.

    Элемент

    <xsl:if>
    во многом похож на оператор
    if-then
    в языках программирования. Однако не существует оператора
    <xsl:else>
    для формирования конструкций
    if-then-else
    — для этого служит элемент
    <xsl:choose>
    .

    В листинге 5.1 я перечисляю планеты в

    planets.xml
    одну за другой и добавляю горизонтальное правило HTML, элемент <HR> (horizontal rule), после последнего элемента — но только после последнего. При помощи
    <xsl:if>
    это можно сделать так.

    Листинг 5.1. Применение <xsl:if>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         Planets

        </TITLE>

       </HEAD>

       <BODY>

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

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <P>

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

       is planet number <xsl:value-of select="position()"/> from the sun.

      </P>

      <xsl:if test="position() = last()"><HR/><xsl:if>

     </xsl:template>

    </xsl:stylesheet>

    Вот результат — как видите, элемент <HR> появляется только после последней перечисленной планеты:

    <HTML>

     <HEAD>

      <TITLE>

       Planets

      </TITLE>

     </HEAD>

     <BODY>

      <P>

       Mercury is planet number 1 from the sun.

      </P>

      <P>

       Venus is planet number 2 from the sun.

      </P>

      <P>

       Earth is planet number 3 from the sun.

      </P>

      <HR>

     </BODY>

    </HTML>

    Рассмотрим еще один пример — преобразование XML-XML, в котором перечисляются планеты из

    planets.xml
    . Однако я хочу, чтобы выводилось не просто «The first three planets are: Mercury Venus Earth» (первые три планеты: Меркурий Венера Земля), a «The first three planets are: Mercury, Venus, and Earth». Необходимые знаки пунктуации можно добавить, определяя текущий элемент при помощи функции
    position
    и проверяя позицию при помощи
    <xsl:if>
    (листинг 5.2).

    Листинг 5.2. Второй пример применения <xsl:if>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

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

      <xsl:if test="position()!=last()">, </xsl:if>

      <xsl:if test="position()=last()-1">and </xsl:if>

      <xsl:if test=position()=last()">.</xsl:if>

     </xsl:template>

    </xsl:stylesheet>

    Вот результат:

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

    <DOCUMENT>

     <TITLE>

      The Planets

     </TITLE>

     <PLANETS>

      The first three planets are: Mercury, Venus, and Earth

     </PLANETS>

    </DOCUMENT>

    Как видите, я смог добавить правильные знаки пунктуации, определяя место в документе при помощи

    <xsl:if>
    .

    При помощи

    <xsl:if>
    можно также обнаруживать ошибки во время преобразования. Например, при помощи
    <xsl:if>
    можно вывести сообщение, есть ли в
    planets.xml
    элемент
    <NAME>
    (листинг 5.3).

    Листинг 5.3. Обнаружение ошибок при помощи <xsl:if>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

    <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

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

      <xsl:if test="position()!=last()">. </xsl:if>

      <xsl:if test="position()=last()-1">and </xsl:if>

      <xsl:if test="position()=last()">.</xsl:if>

     </xsl:template>

    </xsl:stylesheet>

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

    <NAME>
    пустым:

    <?xml version="1.0"?>

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

    <PLANETS>

     <PLANET>

      <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>

      <NAME></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>

     .

     .

     .

    Вот как происходит обработка примера в Xalan:

    C:\planets>java org.apache.xalan.xslt.Process -IN planets.xml -XSL errors.xsl -OUT new.xml

    file:///C:/XSL/w.xsl: Line 18: Column 38: Each planet must have a name!

    XSLT Error (javax.xml.transform.TransformerException): Stylesheet directed termination

    Если вы знакомы с конструкцией

    if
    в языках программирования, вы знаете, что инструкция
    if
    обычно сопровождается инструкцией
    else
    , которая выполняется при ложности условия в
    if
    . Но в XSLT нет элемента
    <xsl:else>
    . Для задания альтернативных ветвей выполнения XSLT служит элемент
    <xsl:choose>
    .

    Элементы <xsl:choose>, <xsl:when> и <xsl:otherwise>

    Элемент

    <xsl:choose>
    похож на оператор Java
    switch
    , который позволяет сравнивать значение условия с несколькими возможными вариантами.

    У элемента

    <xsl:choose>
    нет атрибутов. Он содержит один или более элементов
    <xsl:when>
    и (не обязательно) один элемент
    <xsl:otherwise>
    , который в случае применения должен стоять последним.

    Вот как это работает: в элемент

    <xsl:choose>
    заключаются элементы
    <xsl:when>
    , каждый с условием
    true/false
    . Применяется тело шаблона в первом элементе
    <xsl:when>
    , чье условие имеет значение
    true
    , все остальные не применяются. Последним элементом внутри элемента
    <xsl:choose>
    может быть элемент
    <xsl:otherwise>
    тело шаблона внутри этого элемента применяется, если ни одно из предыдущих условий
    <xsl:when>
    не имело значения
    true
    :

    <xsl:choose>

     <xsl:when test="expression1">

      <!--template-body 1-->

     </xsl:when>

     <xsl:when test="expression">

      <!-- template-body 2-->

     </xsl:when>

     <xsl:when test="expression3">

      <!--template-body 3-->

     </xsl:when>

     <xsl:otherwise>

      <!--template body 4-->

     </xsl:otherwise>

    </xsl:choose>

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

    <xsl:if>
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

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

      <xsl:if test="position()!=last()">, </xsl:if>

      <xsl:if test="position()=last()-1>and </xsl:if>

      <xsl:if test="position()=last()">.</xsl:if>

     </xsl:template>

    </xsl:stylesheet>

    Теперь то же самое можно сделать при помощи единственного элемента

    <xsl:choose>
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xml:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

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

      <xsl:choose>

       .

       .

       .

      </xsl:choose>

     </xsl:template>

    </xsl:stylesheet>

    Нам нужно проверить, в каком месте документа мы находимся, при помощи включения нескольких элементов

    <xsl:when>
    . У этого элемента только один атрибут:

    • 

    test
    (обязательный). Принимает логическое (
    Boolean
    ) значение (
    true/false
    ) проверяемого условия.

    Элемент

    <xsl:when>
    содержит тело шаблона.

    Атрибут проверки принимает значение true/false выражения, определяющего, будет ли применяться заключенное в элементе

    <xsl:when>
    тело шаблона или нет. Например, вот как я добавил элементы
    <xsl:when>
    с соответствующими знаками пунктуации для всех планет, кроме последней:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

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

      <xsl:choose>

       <xsl:when test="position()!=last()">, </xsl:when>

       <xsl:when test="position()=last()-1">and </xsl:when>

       .

       .

       .

      </xsl:choose>

     </xsl:template>

    </xsl:stylesheet>

    Эти два элемента <xsl:when> выбирают все элементы

    <PLANET>
    за исключением последнего, поэтому элемент
    <xsl:otherwise>
    можно применить для последнего элемента
    <PLANET>
    . Тело шаблона в этом элементе будет применено, если ни в одном элементе
    <xsl:when>
    в элементе
    <xsl:choose>
    условие не примет значение
    true
    .

    У элемента

    <xsl:otherwise>
    нет атрибутов, и он содержит тело шаблона. Вот как это выглядит в листинге 5.4.

    Листинг 5.4. Применение <xsl:choose>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

        The first three planets are: <xsl:apply-templates select="PLANET"/>

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

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

      <xsl:choose>

       <xsl:when test="position()!=last()">, </xsl:when>

       <xsl:when test="position()=last()-1">and </xsl:when>

       <xsl:otherwise>.</xsl:otherwise>

      </xsl:choose>

     </xsl:template>

    </xsl:stylesheet>

    Вот как это работает; этот код дает тот же результат, что и код, проверяющий позицию элементов

    <PLANET>
    при помощи
    <xsl:if>
    :

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

    <DOCUMENT>

     <TITLE>

      The Planets

     </TITLE>

     <PLANETS>

      The first three planets are: Mercury, Venus, and Earth.

     </PLANETS>

    </DOCUMENT>

    Вот еще один пример преобразования XML-XML. В этом случае я преобразую

    planets.xml
    в новый XML-документ, сохраняя только название каждой планеты и добавляя описание:

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

    <DOCUMENT>

     <TITLE>

      The Planets

     </TITLE>

     <PLANETS>

      <PLANET>

       <NAME>Mercury</NAME>

       <DESCRIPTION>Hottest</DESCRIPTION>

      </PLANET>

      <PLANET>

       <NAME>Venus</NAME>

       <DESCRIPTION>Hot</DESCRIPTION>

      </PLANET>

      <PLANET>

       <NAME>Earth</NAME>

       <DESCRIPTION>OK</DESCRIPTION>

      </PLANET>

     </PLANETS>

    </DOCUMENT>

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

    <NAME>
    , то есть заключенный в нем текст (заметьте, что такого рода строки в XSLT учитывают регистр) (листинг 5.5).

    Листинг 5.5. Второй пример <xsl:choose>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <DOCUMENT>

       <TITLE>

        The Planets

       </TITLE>

       <PLANETS>

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

       </PLANETS>

      </DOCUMENT>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="NAME[not(text())]">

       <xsl:message terminate="yes">

        Each planet must have a name!

       </xsl:message>

      </xsl:if>

      <PLANET>

       <NAME>

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

       </NAME>

       <DESCRIPTION>

        <xsl:choose>

         <xsl:when test="NAME='Mercury'">Hottest</xsl:when>

         <xsl:when test="NAME='Venus'">Hot</xsl:when>

         <xsl:when test="NAME='Earth'">OK</xsl:when>

        </xsl:choose>

       </DESCRIPTION>

      </PLANET>

     </xsl:template>

    </xsl:stylesheet>

    Вот и все.

    Предположим теперь, что нам нужно добавить в каждый элемент

    <PLANET>
    атрибут
    COLOR
    :

    <?xml version="1.0"?>

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

    <PLANETS>

     <PLANET COLOR="RED">

      <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="WHITE">

      <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="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>

    Отобразить названия различных планет при помощи элемента

    <xsl:choose>
    , отформатированные по-разному при помощи тегов HTML
    <В>
    ,
    <I>
    и
    <U>
    в зависимости от значения атрибута
    COLOR
    , можно следующим образом (листинг 5.6).

    Листинг 5.6. Форматирование при помощи <xsl:choose>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         Planets

        </TITLE>

       </HEAD>

       <BODY>

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

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:choose>

       <xsl:when test="@COLOR = 'RED'">

        <В>

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

        </B>

       </xsl:when>

       <xsl:when test="@COLOR = 'WHITE'">

        <I>

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

        </I>

       </xsl:when>

       <xsl:when test="@COLOR = 'BLUE'">

        <U>

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

        </U>

       </xsl:when>

       <xsl:otherwise>

       <PRE>

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

       </PRE>

      </xsl:otherwise>

     </xsl:choose>

    </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       Planets

      </TITLE>

     </HEAD>

     <BODY>

      <B>Mercury</B>

      <I>Venus</I>

      <U>Earth</U>

     </BODY>

    </HTML>

    Как вы видели, при помощи

    <xsl:if>
    можно проверять единственное условие, а при помощи
    <xsl:choose>
    — несколько; аналогичные конструкции присутствуют в большинстве языков программирования. Кроме подобных этим условных операторов, в большей части языков программирования существуют также операторы цикла, и в XSLT содержится нечто похожее — элемент
    <xsl:for-each>
    .

    Элемент <xsl:for-each>

    Элемент

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

    <XSL:FOR-EACH> ПРОТИВ <XSL:APPLY-TEMPLATES>

    Вы могли заметить, что это описание практически такое же, как и у элемента <xsl:apply-templates>, и я сравню элементы <xsl:for-each> и <xsl:apply-templates> через несколько страниц.

    У элемента

    <xsl:for-each>
    один атрибут:

    • 

    select
    (обязательный). Принимает значение выражения XPath, возвращающее набор узлов, который нужно обработать в цикле.

    Элемент может содержать ноль или более элементов

    <xsl:sort>
    , за которыми следует тело шаблона. Работу с элементом
    <xsl:sort>
    мы изучим позже в этой главе.

    В теле шаблона функция

    position
    возвращает позицию текущего узла в наборе узлов, a
    last
    возвращает число узлов в наборе. Если
    <xsl:sort>
    не используется, узлы обрабатываются в порядке документа (в порядке, в котором они перечислены в документе); если же используется элемент
    <xsl:sort>
    , набор узлов будет сначала отсортирован в порядке, заданном этим элементом.

    Предположим, нам нужно отформатировать все названия планет, заключив их в элементы HTML

    <Р>
    , — это можно сделать следующим образом:

    <xsl:template match="PLANET">

     <Р>

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

     </P>

    </xsl:template>

    Но что делать, если у некоторых планет по два названия, как, например:

    <PLANET>

     <NAME>Mercury</NAME>

     <NAME>Closest planet to the sun</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>

    Это проблема, поскольку атрибут

    select
    элемента
    <xsl:value-of>
    сам по себе выберет только первый элемент
    <NAME>
    . Чтобы пройти в цикле все возможные варианты, вместо него следует применить элемент
    <xsl:for-each>
    (листинг 5.7).

    Листинг 5.7. Применение <xsl:for-each>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="PLANETS">

      <HTML>

       <xsl:apply-templates/>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

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

       <P>

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

       </P>

      </xsl:for-each>

     </xsl:template>

    </xsl:stylesheet>

    Эта таблица стилей охватывает все элементы

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

    <HTML>

     <P>Mercury</P>

     <P>Closest planet to the sun</P>

     <P>Venus</P>

     <P>Earth</P>

    </HTML>

    Вот еще один пример, впервые появившийся в главе 3, «Создание и применение шаблонов», где при помощи элемента

    <xsl:for-each>
    в цикле перебирались все атрибуты элемента:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="*">

      <xsl:copy>

       <xsl:for-each select="@*">

        <xsl:copy/>

       </xsl:for-each>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Следующий пример появился в главе 2, «Создание и применение таблиц стилей». Это упрощенная таблица стилей, в которой нельзя использовать какие-либо элементы высокого уровня, то есть нельзя использовать

    <xsl:template>
    или
    <xsl:apply-templates>
    , однако можно пройти по узлам в цикле при помощи
    <xsl:for-each>
    :

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

     <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:for-each select="//PLANET">

        <TR>

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

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

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

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

        </TR>

       </xsl:for-each>

      </TABLE>

     </BODY>

    </HTML>

    Эта упрощенная таблица стилей форматирует

    planets.xml
    в
    planets.html
    практически так же хорошо, как и шаблон, использующий
    <xsl:apply-templates>
    , в связи с чем появляется интересный вопрос: когда следует для прохода по узлам применять
    <xsl:for-each>
    , а когда
    <xsl:apply-templates>
    ?

    Как правило,

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

    Элемент

    <xsl:for-each>
    работает во многом так же, как и
    <xsl:apply-templates>
    ; можно даже вкладывать шаблоны при помощи
    <xsl:for-each>
    , как это делается при помощи последовательных элементов
    <xsl:apply-templates>
    . В листинге 5.8 я прохожу в цикле по каждому элементу
    <PLANET>
    , а затем во вложенном в него цикле по всем элементам, содержащимся в элементе
    <PLANET>
    , перечисляя их данные из элементов <
    DATA>
    следующим образом.

    Листинг 5.8. Второй пример <xsl:for-each>

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="PLANETS">

      <PLANETS>

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

        <PLANET>

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

          <DATA>

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

          </DATA>

         </xsl:for-each>

        </PLANET>

       </xsl:for-each>

      </PLANETS>

     </xsl:template>

    </xsl:stylesheet>

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

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

    <PLANETS>

     <PLANET>

      <DATA>Mercury</DATA>

      <DATA>.0553</DATA>

      <DATA>58.65</DATA>

      <DATA>1516</DATA>

      <DATA>.983</DATA>

      <DATA>43.4</DATA>

     </PLANET>

     <PLANET>

      <DATA>Venus</DATA>

      <DATA>.815</DATA>

      <DATA>116.75</DATA>

      <DATA>3716</DATA>

      <DATA>.943</DATA>

      <DATA>66.8</DATA>

     </PLANET>

     <PLANET>

      <DATA>Earth</DATA>

      <DATA>1</DATA>

      <DATA>1</DATA>

      <DATA>2107</DATA>

      <DATA>1</DATA>

      <DATA>128.4</DATA>

     </PLANET>

    </PLANETS>

    Сортирующие элементы

    При помощи элемента

    <xsl:sort>
    можно сортировать узлы. Этот элемент устанавливает порядок обработки узлов для
    <xsl:apply-templates>
    и
    <xsl:for-each>
    . В следующем списке перечислены атрибуты
    <xsl:sort>
    :

    • 

    select
    (необязательный). Принимает значение выражения XPath, возвращающего набор узлов для сортировки. По умолчанию — «string(.)»;

    • 

    order
    (необязательный). Задает порядок сортировки, устанавливается в «ascending» (по возрастанию) или «descending» (по убыванию);

    • 

    case-order
    (необязательный). Определяет, будут ли буквы в верхнем регистре располагаться перед буквами в нижнем регистре. Устанавливается в «upper-first» (сначала верхний) или «lower-first» (сначала нижний);

    • 

    lang
    (необязательный). Задает язык, чьи соглашения о сортировке будут применяться. Устанавливается в код языка, допустимый в атрибуте
    xml:lang
    ;

    • 

    data-type
    (необязательный). Определяет, будет ли сортировка вестись в алфавитном или числовом порядке. Устанавливается в «
    text
    » (текст), «
    number
    » (число) или в
    QName
    .

    Этот элемент не включает содержимое. Его следует применять внутри элементов <xsl:apply-templates> или <xsl:for-each> для сортировки наборов узлов, с которыми работают эти элементы.

    В листинге 5.9 я только отсортирую элементы

    <PLANET>
    в
    planets.xml
    в возрастающем алфавитном порядке по их именам, используя
    <xsl:for-each>
    в упрощенной таблице стилей.

    Листинг 5.9. Сортировка данных

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

     <HEAD>

      <TITLE>

       The Sorted Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Sorted Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

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

        <xsl:sort/>

        <TR>

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

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

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

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

        </TR>

       </xsl:for-each>

      </TABLE>

     </BODY>

    </HTML>

    А вот результат. Обратите внимание на то, что планеты действительно отсортированы как Earth, Mercury и затем Venus:

    <HTML>

     <HEAD>

      <TITLE>

       The Sorted Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Sorted 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</TD>

        <TD>2107</TD>

        <TD>1</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553</TD>

        <TD>1516</TD>

        <TD>58.65</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815</TD>

        <TD>3716</TD>

        <TD>116.75</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Вид документа показан на рис. 5.1.

    Рис. 5.1. Сортировка при помощи упрощенного шаблона


    При помощи атрибута select можно указать, что нужно сортировать. Например, таким образом можно отсортировать планеты по плотности (листинг 5.10).

    Листинг 5.10. Сортировка планет по плотности

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         Planets

        </TITLE>

       </HEAD>

       <BODY>

        <H1>Planets sorted by density</H1>

        <TABLE>

         <TR>

          <TD>Planet</TD>

          <TD>Mass</TD>

          <TD>Day</TD>

          <TD>Density</TD>

         </TR>

         <xsl:apply-templates>

          <xsl:sort select="DENSITY"/>

         </xsl:apply-templates>

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

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

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

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

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

      </TR>

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       Planets

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       Planets sorted by density

      </Н1>

      <TABLE>

       <TR>

        <TD>Planet</TD>

        <TD>Mass</TD>

        <TD>Day</TD>

        <TD>Density</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815</TD>

        <TD>116.75</TD>

        <TD>.943</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553</TD>

        <TD>58.65</TD>

        <TD>.983</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

        <TD>1</TD>

        <TD>1</TD>

        <TD>1</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    По умолчанию

    <xsl:sort>
    производит сортировку в алфавитном порядке, что означает, что «10» располагается перед «2». Для осуществления сортировки в числовом порядке установите атрибут типа данных в «number»:

    <xsl:sort data-type="number" select="DENSITY"/>

    Убывающий порядок сортировки задается установкой у элемента

    <xsl:sort>
    атрибута order в «descending». Можно также сортировать по значениям атрибута, например:

    <xsl:apply-templates select="PLANETS>

     <xsl:sort select="@SIZE"/>

    </xsl:apply-templates>

    НОВОЕ В XSLT 2.0

    Одним из важных преимуществ XSLT 2.0 является поддержка схем XML, и W3C планирует внести в XSLT 2.0 возможность сортировки по любому типу данных, определенному в схеме документа, так же, как сейчас можно сортировать по строкам или числам.

    Сортировка по нескольким критериям

    Стоит отметить, что при сортировке можно применять несколько критериев — для этого просто примените несколько элементов

    <xsl:sort>
    . Первый элемент
    <xsl:sort>
     отсортирует данные по основному критерию, следующий элемент — по следующему основному критерию и т.д. Вот, например, как внутри элемента
    <xsl:apply-templates>
    можно отсортировать планеты сначала по расстоянию, а затем по плотности (при этом планеты с одинаковым расстоянием от Солнца будут отсортированы по плотности):

    <xsl:apply-templates>

     <xsl:sort select="DISTANCE"/>

     <xsl:sort select="DENSITY"/>

    </xsl:apply-templates>

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

    Элемент <xsl:number>

    При помощи элемента

    <xsl:number>
    узлу в результирующем документе присваивается очередной номер. Например, можно перенумеровать абзацы в контракте или строфы в поэме. Можно даже перенумеровать части документа несколькими уровнями, как, например, «Paragraph 3.2.5.1.» и т.п.

    Элемент

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

    • 

    level
    (необязательный). Определяет, как будут присваиваться последовательные числа. Устанавливается в «single» (один), «multiple» (несколько) или «any» (любой). Значение по умолчанию — «single»;

    • 

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

    • 

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

    • 

    value
    (необязательный). Форматируемое число;

    • 

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

    • 

    lang
    (необязательный). Определяет язык, чьи соглашения следует использовать для нумерации. Устанавливается в код языка, который можно применять в атрибуте
    xml:lang
    ;

    • 

    letter-value
    (необязательный). Позволяет выбрать различные схемы нумерации. устанавливается в «alphabetical» (алфавитная) или «traditional» (обычная);

    • 

    grouping-separator
    (необязательный). Символ для разделения групп разрядов — например, запятая. Устанавливается в шаблон значений атрибутов, возвращающий единственный символ;

    • 

    grouping-size
    (необязательный). Количество разрядов в каждой группе — определяет место применения разделителя групп разрядов. Устанавливается в шаблон значений атрибутов, возвращающий число. 

    СОВЕТ ПО НУМЕРАЦИИ

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

    Существует три основных способа нумерации, в зависимости от установки атрибута уровня (

    level
    ): «
    single
    », «
    multiple
    » или «
    any
    ». В следующих разделах мы по очереди рассмотрим каждую из этих схем, начав с одноуровневой нумерации, которая установлена по умолчанию.

    Одноуровневая нумерация

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

    planets.xml
    .

    Листинг 5.11. Одноуровневая нумерация

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

     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

        </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 match="PLANET">

      <TR>

       <TD><xsl:number/>. <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>1. Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>2. Venus</TD>

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

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>3. Earth</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Этот результат показан на рис. 5.2.

    Рис. 5.2. Одноуровневая нумерация элементов 


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

    <xsl:number format="a"/>
    , планетам были бы присвоены буквы a, b и с:

    <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>a. Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>b. Venus</TD>

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

    <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       .

       .

       .

    Ниже приведены возможные в атрибуте

    format
    лексемы и соответствующие им виды нумерации:

    • 1 порождает последовательность 1, 2, 3…;

    • 01 порождает последовательность 01, 02, 03…;

    • другие числа Unicode в других системах счисления порождают последовательности аналогично двум предыдущим правилам;

    • а порождает последовательность a, b, с…, аа, ab…;

    • А порождает последовательность А, В, С…, АА, АВ…;

    • i порождает последовательность i, ii, iii, iv…, ix, x, xi, xii…;

    • I порождает последовательность I, II, III, IV…, IX, X, XI, XII…

    Нумерация на произвольных уровнях

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

    <NAME>
    , и вам может быть нужно просто трактовать документ как поток данных, подсчитывая встречающиеся элементы
    <NAME>
    .

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

    planets.xml
    элементы
    <NAME>
    встречаются на различных уровнях:

    <?xml version="1.0"?>

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

     <PLANETS>

      <TITLE>

       <NAME>Planets Table</NAME>

      </TITLE>

      <PLANET>

       <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>

       <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><!--В перигелии-->

      </PLANET>

      .

      .

      .

    Чтобы сосчитать общее число элементов

    <NAME>
    , в таблице стилей можно установить атрибут
    level
    в «any» (листинг 5.12).

    Листинг 5.12. Нумерация на произвольных уровнях

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

     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

        </H1>

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

        <TABLE BORDER="2">

         <TR>

          <TD>Name</TD>

          <TD>Mass</TD>

          <TD>Radius</TD>

          <TD>Day</TD>

         </TR>

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

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

       <TD><xsl:apply-templates 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="TITLE">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="NAME">

      <xsl:number level="any" count="NAME"/>. <xsl:value-of select="."/>

     </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>

    Получаем результат (заметьте, что текст каждого элемента <NAME>, независимо от его уровня в документе, пронумерован):

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      1. Planets Table

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>2. Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>3. Venus</TD>

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

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>4. Earth</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    При помощи атрибута

    from
    можно указать, с какого узла-предка начинать отсчет; например, если установить узел-предок в элемент
    <PLANET>
    так:

    <xsl:number level="any" count="NAME" from="PLANET"/>

    то процессор XSLT осуществит обратный просмотр только до первого предка

    <PLANET>
    и начнет нумерацию с этой точки документа.

    Многоуровневая нумерация

    Элемент

    <xsl:number>
    также поддерживает многоуровневую нумерацию — такую как 3.1.2.5 и т. п. Для работы с ней нужно установить атрибут
    level
    в «multiple». При помощи атрибута
    count
    можно указать, узлы какого типа вы хотите нумеровать, установив этот атрибут в образец, например: "
    PART|CHAPTER|PARAGRAPH
    ". При обработке элементов
    <xsl:number>
    процессор XSLT нумерует узлы в соответствии с иерархией документа.

    В примере я нумерую каждый уровень в иерархии элементов

    planets.xml
    , установив атрибут
    count
    в «*» для выбора всех элементов. Можно также указать формат нумерации при помощи атрибута
    format
    . При многоуровневой нумерации атрибут
    format
    задает формат для различных уровней, например «1.1.1.» задает нумерацию 1., 2., … и т.д. для узлов верхнего уровня, 1.1., 1.2., … и т.д. для узлов уровнем ниже и 1.2.1., 1.2.2., … и т. д. для следующего уровня вниз. Вот как выглядит таблица стилей для этого примера в листинге 5.13.

    Листинг 5.13. Многоуровневая нумерация

    <?xml version="1.0"?>

    <хsl:stylesheet version="1.0"

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

     <xsl:output method="xml"/>

     <xsl:template match="node()">

      <xsl:copy>

       <xsl:number format="1.1.1." level="multiple" count="*"/>

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

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Вот результат преобразования planets.xml в новый XML-документ, в котором перенумерованы все уровни элементов в соответствии с иерархией документа:

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

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

    <PLANETS>1.

     <PLANET>1.1.

      <NAME>1.1.1. Mercury</NAME>

      <MASS>1.1.2. .0553</MASS>

      <DAY>1.1.3. 58.65</DAY>

      <RADIUS>1.1.4. 1516</RADIUS>

      <DENSITY>1.1.5. .983</DENSITY>

      <DISTANCE>1.1.6. 43.4</DISTANCE><!--В перигелии-->

     </PLANET>

     <PLANET>1.2.

      <NAME>1.2.1. Venus</NAME>

      <MASS>1.2.2. .815</MASS>

      <DAY>1.2.3. 116.75</DAY>

      <RADIUS>1.2.4. 3716</RADIUS>

      <DENSITY>1.2.5. .943</DENSITY>

      <DISTANCE>1.2.6 66.8</DISTANCE><!--В перигелии-->

     </PLANET>

     <PLANET>1.3.

      <NAME>1.3.1. Earth</NAME>

      <MASS>1.3.2. 1</MASS>

      <DAY>1.3.3. 1</DAY>

      <RADIUS>1.3.4. 2107</RADIUS>

      <DENSITY>1.3.5. 1</DENSITY>

      <DISTANCE>1.3.6. 128.4</DISTANCE><!--В перигелии-->

     </PLANET>

    </PLANETS>

    На этом мы завершаем рассмотрение нумерации документов и переходим к последней теме этой главы — расширяемости XSLT.

    Расширяемость XSLT

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

    <saxon:while>
    , реализуя в XSLT стандартный для программирования цикл
    while
    (до тех пор, пока). Xalan представил такие элементы, как
    <redirect:write>
    , для поддержки вывода нескольких документов. А процессор MSXML3 от Microsoft позволяет писать функции на языках таких сценариев, как JavaScript, и затем вызывать их и выполнять их код.

    Можно представить, с каким беспокойством на это смотрит W3C. Его работа, в принципе, заключается в стандартизации работы таких языков, как XSLT, но производители постоянно представляли свои собственные, нестандартные расширения в виде новых элементов и функций. С другой стороны, W3C не может предугадать все новые элементы и функции, поэтому консорциум начал работать над стандартизацией способов включения функций расширения и элементов в XSLT. Расширения должны удовлетворять ряду общих правил:

    • расширения должны использовать пространства имен во избежание конфликтов с элементами XSL;

    • процессор XSLT должен быть в состоянии распознать применение расширения — и в случае ошибки расширения реагировать хорошо определенным способом;

    • таблица стилей должна быть в состоянии проверить, доступно ли определенное расширение, и если нет, вернуться назад.

    НОВОЕ В XSLT 2.0

    Легко представить сложности W3C даже с этими общими правилами, и комитет XSLT 2.0 собирается исследовать возможность реализации всех расширений на «чистом» XSLT, вообще не прибегая к каким-либо внешним языкам программирования.

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

    В XSLT 1.0 проверить доступность функции расширения можно при помощи функции

    function-available
    , а доступность элемента расширения — при помощи функции
    element-available
    .

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

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

    ИНИЦИАТИВА EXSLT

    Теперь, после того, как механизмы расширения в рабочем проекте XSLT 1.1 были отложены до XSLT 2.0, роль других разнообразных попыток стандартизации расширений XSLT значительно повысилась. Познакомьтесь, например, с EXSLT на www.exslt.org. EXSLT — это инициатива открытого сообщества, работающего над стандартизацией расширений XSLT.

    Функции расширения

    В XSLT 1.0 W3C определил способ отделения функций расширения от встроенных функций, установив требование, чтобы для обращения к функциям расширения использовались имена с заданным пространством имен, как в

    starpowder:calculate()
    . В XSLT 1.0 также имеется функция
    function-available()
    для проверки наличия функции по ее имени.

    В рабочем проекту XSLT 1.1 на функции расширения были наложены некоторые дополнительные ограничения:

    • функции расширения должны работать как встроенные функции;

    • для Java и ECMAScript должны быть реализованы привязки к языку;

    • механизм должен позволять естественное расширение для поддержки других языков в будущем;

    • для реализации переносимой привязки функции расширения для любого конкретного языка не должен быть нужен процессор;

    • процессор, реализующий функции расширения для любого языка, чья привязка обеспечивается спецификацией XSLT, должен соответствовать этим языкам;

    • должны быть разрешены как встроенные реализации функций расширения, так и внешние;

    • в функции расширения должно быть возможно передавать аргументы всех типов данных XPath;

    • функции расширения должны иметь возможность возвращать в качестве результата все типы данных XPath;

    • функции расширения должны иметь возможность создавать и возвращать наборы узлов фрагментов XML;

    • должна иметься возможность включать или импортировать функции расширения из другой таблицы стилей;

    • при неоднозначности выбора реализации функции расширения процессор должен выдать ошибку и прекратить работу;

    • процессор должен преобразовывать аргументы способом, согласованным со встроенными функциями;

    • функции расширения должны быть способны вернуть объект любого типа основного языка;

    • должна существовать возможность передать в функцию расширения объект любого типа основного языка.

    Вплоть до недавнего времени процессоры XSLT полностью самостоятельно определяли способ реализации функций расширения. Например, в Saxon и Xalan существует возможность непосредственно выполнять код Java, если определить пространство имен, задающее класс Java в качестве последней части URI. Я поступил так в следующем случае, определив пространство имен

    Date
    , соответствующее классу Java
    Date
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     xmlns:Date="http://www.saxon.com/java/java.util.Date">

     .

     .

     .

    После этого я могу воспользоваться такими функциями класса

    Date
    Java, как
    toString
    и
    new
    , для того чтобы заключить текущую дату в элементы заголовка
    <Н1>
    HTML в выходном документе (листинг 5.14).

    Листинг 5.14. Применение функций Date Java

    <?xml version="1.0"?>

    <xsl:stylesheel version="1.0"

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

     xmlns:Date="http://www.saxon.com/java/java.util.Date">

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         The Planets Table

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         The Planets Table

        </H1>

        <BR/>

        <H1>

         <xsl:value-of select="Date:toString(Date:new())"/>

        </H1>

        <TABLE BORDER="2">

         <TD>Name</TD>

         <TD>Mass</TD>

         <TD>Radius</TD>

         <TD>Day</TD>

         <xsl:apply-templates/>

        </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:applу-templates select="DAY"/></TD>

      </TR>

     </xsl:template>


     <xsl:template match="MASS">

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

     </xsl:template>


     <xsl:template match="RADIUS">

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

     </xsl:template>


     <xsl:template match="DAY">

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

     </xsl:template>

    </xsl:stylesheet>

    Результат применения этой функции приведён на рис. 5.3.

    Рис. 5.3. Применение функции расширения 


    Вот работоспособная схема и заодно веский повод включить Java в XSLT. Тем не менее, в XSLT 1.1 был представлен элемент

    <xsl:script>
    , который, по всей видимости, будет включен и в XSLT 2.0.

    Элемент <xsl:script>

    Элемент

    <xsl:script>
    был определен в рабочем проекте XSLT 1.1, он предоставляет хорошо определенный способ связи функций расширения с таблицами стилей XSLT. Это элемент верхнего уровня, обладающий следующими атрибутами:

    • 

    implements-prefix
    (необязательный). Задает имя пространства имен функции расширения, которую реализует этот элемент. Принимает значение
    NCNAME
    ;

    • 

    language
    (необязательный). Задает язык, используемый функцией расширения. Устанавливается в «
    ecmascript
    » (стандарт JavaScript), «
    javascript
    », «
    java
    » или QNAME, не являющееся NCNAME;

    • 

    src
    (необязательный). Предоставляет URI, в котором реализована функция расширения. Например, это может быть класс Java;

    • 

    archive
    (необязательный). Задает архивы, которые необходимо загрузить перед запуском функции расширения, если они есть. Принимает значения списка URI, разделенного символами-разделителями.

    Элемент содержит символьные данные (Microsoft использует раздел

    CDATA
    ), реализующие функцию или функции расширения.

    Как теперь связать функцию, определенную в элементе

    <xsl:script>
    , с вашей таблицей стилей XSLT? Сначала создайте в своей таблице стилей элемент
    <xsl:script>
    как элемент верхнего уровня, затем поместите в него функции, которые вы хотите определить. В приведенном ниже примере я определяю две функции JavaScript,
    makeMoney
    (сделать деньги) и
    makeMoreMoney
    (сделать еще больше денег), в элементе
    <xsl:script>
    , реализующем пространство имен расширений «
    starpowder
    »:

    <xsl:script implements prefix="starpowder" language="javascript">

    function makeMoney(e) {

     .

     .

     .

    }


    function makeMoreMoney(e) {

     .

     .

     .

    }

    </xsl:script>

    В зависимости от вашего процессора XSLT, может оказаться хорошим решением заключить такого рода сценарии в раздел CDATA:

    <xsl:script implements-prefix="starpowder" language="javascript">

     <![CDATA[

      function makeMoney(e) {

       .

       .

       .

      }


      function makeMoreMoney(e) {

       .

       .

       .

      }

     ]]>

    </xsl:script>

    Теперь при помощи пространства имен «

    starpowder
    » можно указать, что вызывается функция расширения:

    <CASH>

     <xsl:value-of select="starpowder:makeMoney(1000000)"/>

    </CASH>

    Вот и все (если ваш процессор XSLT это поддерживает). Если вместо сценария вы хотите указать класс Java, воспользуйтесь атрибутом

    src
    :

    <xsl:script implements-prefix="starpowder" src="java:com.MakeMoney" language="java">

    </xsl:script>
     

    РАБОТА С ВНЕШНИМИ РЕСУРСАМИ

    Атрибут src также используется, если есть архив подпрограмм JavaScript, как, например, src="archives.js".

    Из всех известных мне процессоров XSLT элемент

    <xsl:script>
    реализует только процессор Microsoft MSXML3. Информация о работе со сценариями для написания функций расширения для Internet Explorer приведена на web-узле Microsoft (в данный момент это страница по адресу http://msdn.microsoft.com/xml/xslguide/script-overview.asp, но, кажется, Microsoft меняет структуру web-узла каждые два дня или около того).

    Следующий пример демонстрирует работу

    <xsl:script>
    с Internet Explorer. Я создал функцию JavaScript для преобразования данных о радиусе планет из
    planets.xml
    , приведенных в милях, в километры и последующем выводе этих данных в километрах.

    Как обсуждалось в главе 2 в разделе «Преобразование документов XML при помощи Internet. Explorer», для просмотра XML-документа, использующего таблицу стилей XSL, в Internet Explorer, версии 5.5 и младше в документ необходимо внести некоторые изменения (если только вы не установили последний разборщик MSXML или не используете недавно появившуюся версию браузера 6.0, хотя и в этом случае нужно применять «

    text/xsl
    »). Для начала в таблице стилей XSL используйте тип MIME «
    text/xsl
    », а не «
    text/xml
    ». Я также задал URI для таблицы стилей «
    kilometers.хsl
    » следующим образом (листинг 5.15).

    Листинг 5.15. Установка использования kilometers.xsl для planets.xml в Internet Explorer

    <?xml version="1.0"?>

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

    <PLANETS>

     <PLANET>

      <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><!--В перигелии-->

     </PLANET>

     <PLANET>

      <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>

     .

     .

     .

    Для преобразования таблицы стилей

    kilometers.xsl
    для работы в IE 5.5 или младше я воспользовался пространством имен XSL, которое использует IE, и добавил элемент
    <xsl:script>
    , показав, что я собираюсь писать сценарии на JavaScript. Заметьте, однако, что элемент
    <xsl:script>
    в Internet Explorer не поддерживает атрибут префикса реализации, поэтому я не могу связать функции, определенные в пространстве имен:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

     <xsl:script language="javascript">

      .

      .

      .

     </xsl:script>

     .

     .

     .

    В соответствии с требованиями Internet Explorer, код должен быть заключен в раздел CDATA. Здесь я определил функцию

    milesToKilometers
    , которая принимает узел, читает текст узла в свойстве
    text
    и преобразует текст в число миль при помощи функции JavaScript
    parseInt
    . Далее я умножаю число миль на 1,6, чтобы получить километры, и возвращаю результат:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

     <xsl:script language="javascript">

      <![CDATA[

       function milesToKilometers(e) {

        miles = parseInt(e.text);

        return miles * 1.6;

       }

      ]]>

     </xsl:script>

     .

     .

     .

    Поскольку пока в Internet Explorer нельзя связать пространство имен с функцией расширения, для их вызова используется специальный элемент Microsoft

    <xsl:eval>
    . Ниже показано, как это выглядит в таблице стилей
    kilometers.xsl
    , где я передаю в функцию
    milesToKilometers
    текущий узел
    <RADIUS>
    для преобразования миль в километры. Поскольку IE 5.5 и младше не поддерживают правила по умолчанию (хотя версия 6.0, вышедшая одновременно с подписанием этой книги в печать, поддерживает их, и вам не нужно ничего менять), для этих браузеров я предоставил правило для корневого узла (листинг 5.16).

    Листинг 5.16. kilometers.xsl

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

     <xsl:script language="javascript">

      <![CDATA[

       function milesToKilometers(e) {

        miles = parseInt(e.text);

        return miles * 1.6;

       }

      ]]>

     </xsl:script>


     <xsl:template match="/">

      <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/>

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANETS">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

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

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

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

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

      </TR>

     </xsl:template>


     <xsl:template match="RADIUS">

      <xsl:eval>milesToKilometers(this)</xsl:eval>

     </xsl:template>

    </xsl:stylesheet>

    Вот и все, результат этого преобразования приведен на рис. 5.4.

    Рис. 5.4. Применение функции расширения в Internet Explorer


    Со временем производители будут поставлять все больше и больше функций расширения. Как можно определить, доступна ли заданная функция расширения? Для этого служит функция

    function-available
    .

    Применение функции function-available

    Функция XSLT 1.0

    function-available
    служит для проверки доступности функции. В следующем примере я хочу воспользоваться функцией расширения
    starpowder:calculate
    для математических вычислений, а если она недоступна, я отправляю в результирующий документ текст «Sorry, can't do math today.» (Извините, сегодня математические вычисления не работают.), хотя можно, конечно, прекратить обработку и вывести сообщение об ошибке при помощи элемента
    <xsl:message>
    :

    <xsl:choose xmlns:starpowder="http://www.starpowder.com">

     <xsl:when test="function-available('starpowder:calculate')">

      <xsl:value-of select="starpowder:calculate('2+2')"/>

     </xsl:when>

     <xsl:otherwise>

      <xsl:text>Sorry, can't do math today.</xsl:text>

     </xsl:otherwise>

    </xsl:choose>

    Внешние объекты

    В рабочем проекте XSLT 1.1 для поддержки функций расширения появился новый тип данных — внешний объект (external object). Переменной XSLT, о которой пойдет речь в главе 9, может быть присвоен внешний объект — так же, как и один из четырех типов данных XPath, поддерживаемых в XSLT (строка, число, логическое значение, набор узлов). Внешний объект представляет объект, который создается внешним языком программирования, возвращается функцией расширения и не может быть преобразован в один из четырех типов данных XPath. Тип данных «

    external object
    » был добавлен в XSLT для того, чтобы предоставить вам безопасную «оболочку» для таких данных. Пока еще никто не реализовал поддержку внешних объектов, но это ожидается в скором времени.

    Элементы расширения

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

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

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

    extension-element-prefixes
    в элементе
    <xsl:stylesheet>
    , или атрибут
    xsl:extension-element-prefixes
    в элементе буквального результата или элементе расширения.

    Ниже приведен пример. Xalan позволяет вам создать несколько выходных документов при помощи своего элемента расширения

    <redirect:write>
    . Для того чтобы применить этот элемент, я могу добавить в
    planets.xml
    элементу документа атрибут
    file
    , задав имя файла, в который будет отправлен вывод, как
    redirected.xml
    :

    <?xml version="1.0"?>

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

    <PLANETS file="redirected.xml">

     <PLANET>

      <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>

     .

     .

     .

    Теперь в таблице стилей XSLT, которую я назвал

    redirect.xsl
    , я определяю пространство имен «
    redirect
    » так, чтобы оно соответствовало классу Java, который поддерживает ее в Xalan:
    org.apache.xalan.lib.Redirect
    . Я также устанавливаю атрибут
    extension-element-prefixes
    элемента
    <xsl:stylesheet>
    в значение пространства имен «
    redirect
    »:

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

     version="1.0"

     xmlns:lxslt=http://xml.apache.org/xslt"

     xmlns:redirect="org.apache.xalan.lib.Redirect"

     extension-element-prefixes="redirect">

     .

     .

     .

    В этот момент мне ничто не мешает применить элемент расширения

    <redirect:write>
    для записи вывода в новый файл (в отличие от указанного в командной строке). Например, для того, чтобы отправить в другой файл отформатированное содержимое элемента
    <PLANET>
    , я могу получить имя создаваемого файла из атрибута
    file
    элемента
    <PLANETS>
    и записать данные в этот новый файл:

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

     version="1.0"

     xmlns:lxslt="http://xml.apache.org/xslt"

     xmlns:redirect="org.apache.xalan.lib.Redirect"

     extension-element-prefixes="redirect">

     <lxslt:component prefix="redirect" elements="write open close" functions="">

      <lxslt:script lang="javaclass" src="org.apache.xalan.lib.Redirect"/>

     </lxslt:component>


     <xsl:output method="xml"/>


     <xsl:template match="/">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANETS">

      <redirect:write select="@file">

       <PLANETS>

        <xsl:apply-templates/>

       </PLANETS>

      </redirect:write>

     </xsl:template>


     <xsl:template match="@*|node()">

      <xsl:copy>

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

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Операция завершена; вот как это может выглядеть при использовании Xalan в Windows:

    C:planets>java org.apache.xalan.xslt.Process -IN planets.xml -XSL redirect.xsl -OUT new.xml

    При этом будет создан файл

    redirected.xml
    , который выглядит следующим образом:

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

    <PLANETS>

     <PLANET>

      <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>

     </PLANET>

     <PLANET>

      <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>

     </PLANET>

     .

     .

     .

    Применение функции element-available

    Для проверки доступности элемента служит функция XSLT 1.0

    element-available
    . В следующем примере я проверяю наличие элемента с названием
    <starpowder:calculate>
    :

    <xsl:choose xmlns:starpowder="http://www.starpowder.com">

     <xsl:when test="element-available('starpowder:calculate')">

      <starpowder:calculate xsl:extension-element-prefixes="starpowder"/>

     </xsl:when>

     <xsl:otherwise>

      <xsl:text>Sorry, can't do math today.</xsl:text>

     </xsl:otherwise>

    </xsl:choose>

    Есть еще один способ обработать случай отсутствия элемента расширения — элемент

    <xsl:fallback>
    .

    Элемент <xsl:fallback>

    При помощи элемента XSLT 1.0

    <xsl:fallback>
    можно указать, что следует делать в случае отсутствия элемента расширения. Этот элемент заключается в элемент расширения и используется в случае его недоступности.

    У элемента

    <xsl:fallback>
    нет атрибутов, он содержит тело шаблона.

    В следующем примере я создам элемент

    <xsl:fallback>
    внутри элемента
    <redirect:write>
    из предыдущего примера. В случае отсутствия элемента
    <redirect:write>
    элемент
    <xsl:fallback>
    прекратит обработку и выдаст сообщение:

    <xsl:stylesheet

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

     version="1.0"

     xmlns:lxslt="http://xml.apache.org/xslt"

     xmlns:redirect="org.apache.xalan.lib.Redirect"

     extension-element-prefixes="redirect">

     <lxslt:component prefix="redirect" elements="write open close" functions="">

      <lxslt:script lang="javaclass" src="org.apache.xalan.lib.Redirect"/>

     </lxslt:component>

     <xsl:output method="xml"/>


     <xsl:template match="/">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANETS">

      <redirect:write select="@file">

       <PLANETS>

        <xsl:apply-templates/>

       </PLANETS>

       <xsl:fallback>

        <xsl:message terminate="yes">

         <xsl:text>Could not create multiple output documents.</xsl:text>

        </xsl:message>

       </xsl:fallback>

      </redirect:write>

     </xsl:template>


     <xsl:template match="@*|node()">

      <xsl:copy>

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

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

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







     


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