• Создание шаблона
  • Обработка дочерних узлов
  • Доступ к значениям узлов
  • Создание образцов выбора
  • Выбор применяемого шаблона
  • Чтение значений атрибутов
  • Элемент <xsl:text>
  • Отключение вывода эскейп-последовательностей
  • Написание значений атрибутов
  • Шаблоны значений атрибутов
  • Обработка символов-разделителей
  • Элементы <xsl:strip-space> и <xsl:preserve-space>
  • Автоматическое выравнивание
  • Правила по умолчанию в шаблоне
  • Удаление содержимого
  • Разрешение конфликтов шаблонов
  • Элемент <xsl:copy>
  • Элемент <xsl:copy-of>
  • Элемент <xsl:message>
  • Глава 3

    Создание и применение шаблонов

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

    В главе 2 мы кратко рассмотрели таблицы стилей и основные шаблоны, при помощи которых наши примеры таблиц стилей в действительности могли что-то сделать. В этой главе мы собираемся подробно изучить шаблоны, а в следующей мы увидим, какие виды выражений можно применять для создания образцов выбора в шаблонах, позволяющих находить требуемые узлы. Образцы выбора (match pattern) являются подмножеством всего языка XPath и достаточно сложны для того, чтобы посвятить им отдельную главу.

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

    Создание шаблона

    В главе 2 для выбора узлов в

    planets.xml
    и преобразования этого документа в HTML я создал основной шаблон. Шаблоны в таблицах стилей создаются при помощи элементов
    <xsl:template>
    , задающих правила для требуемых преобразований. Мы создали шаблон, находивший корневой элемент
    <PLANETS>
    по образцу "
    /PLANETS
    ", которому соответствуют все элементы
    <PLANETS>
    , дочерние для корневого узла:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      .

      .

      .

     </xsl:template>

    .

    .

    .

    </xsl:stylesheet>

    Когда процессор XSLT находит узел, удовлетворяющий образцу вашего шаблона, этот узел становится контекстным узлом шаблона, то есть все операции производятся над этим узлом. На текущий узел можно ссылаться при помощи выражения XPath «.». Другие выражения XPath мы рассмотрим в этой главе и в главе 7.

    УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

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

    Внутри шаблона создайте разметку HTML, начинающую требуемую таблицу, — такая прямая вставка разметки называется элементом буквального (literal) результата. Когда процессор встречает литерал, он копирует его в результирующее дерево:

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

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>

     .

     .

     .

    </xsl:stylesheet>

    Однако это правило обрабатывает только элемент

    <PLANETS>
    , но не его дочерние элементы
    <PLANET>
    , содержащие сами данные. В соответствии с правилами шаблона по умолчанию, элементы
    <PLANET>
    будут обрабатываться, если установить шаблон для их выбора. Это, кстати, нежелательно, поскольку я хочу вставить результат обработки элементов
    <PLANET>
    в определенное место в создаваемом файле HTML. Для этого мне потребуется применить элемент
    <xsl:apply-templates>
    .

    Обработка дочерних узлов

    Элемент

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

    Один важный момент часто вызывает затруднения: элемент

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

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

    <xsl:apply-templates>
    туда, куда нам требуется вставить данные из элементов
    <PLANET>
    в таблицу HTML. Я также добавил новый шаблон для обработки элементов
    <PLANET>
    :

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

      .

      .

      .

     </xsl:template>

    </xsl:stylesheet>

    В новом шаблоне, обрабатывающем элементы

    <PLANET>
    , я должен извлечь данные из каждого элемента
    <PLANET>
    , то есть восстановить значения в дочерних элементах элемента
    <PLANET>
    , таких как
    <MASS>
    ,
    <DAY>
    и т.д.:

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

     .

     .

     .

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

    <xsl:value-of>
    .

    Доступ к значениям узлов

    Элемент

    <xsl:value-of>
    записывает в результирующий документ строковое значение выражения; в частности, с его помощью можно возвратить значение узла, которым для элемента будет заключенный в элемент текст. Атрибуту выбора элемента
    <xsl:value-of>
    можно присвоить выражение XPath, задающее узел или набор узлов. В шаблоне, задающем элементы
    <PLANET>
    , обратиться к дочернему элементу
    <MASS>
    можно при помощи выражения XPath "
    child::MASS
    ". Как мы увидим в главе 4, выражения XPath можно писать разными способами: например, вместо "
    child::MASS
    " можно просто написать "
    MASS
    ". Таким образом, получить данные дочерних элементов, таких как
    <MASS>
    ,
    <DAY>
    и т.д., можно следующим способом (листинг 3.1).

    Листинг 3.1. Полная версия planets.xsl

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

        <Н1>

         The Planets Table

        </Н1>

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

    </xsl:stylesheet>

    Создание образцов выбора

    Как вы могли ожидать, исходя из того, что "

    child::MASS
    " можно представить в виде "
    MASS
    " и применения шаблонов, таких как "
    /
    ", "
    /PLANETS
    " и т.д., вплотную познакомиться с созданием образцов выбора не так-то просто — этому посвящена вся глава 4.

    Образцы выбора (match pattern) являются подмножеством полного языка XPath, их можно использовать в элементах

    <xsl:template
    >,
    <xsl:key>
    и
    <xsl:number>
    . В частности, в образец можно установить атрибут match у
    <xsl:template>
    и
    <xsl:key>
    , и атрибуты
    count
    и
    from
    элемента
    <xsl:number>
    . В следующем списке приведен ряд примеров образцов выбора, много других примеров будет показано в главе 4 при подробном обсуждении использования XPath для выбора узлов и атрибутов:

    • "

    /
    " выбирает корневой узел;

    • "

    *
    " выбирает элементы узлов (но не всех узлов, как зачастую ошибочно полагают);

    • "

    PLANET
    " выбирает элементы
    <PLANET>
    ;

    • "

    PLANET/MASS
    " выбирает все элементы
    <MASS>
    , дочерние для элемента
    <PLANET>
    ;

    • "

    //PLANET
    " выбирает все элементы
    <PLANET>
    , производные от корневого узла;

    • "

    .
    " выбирает текущий узел (технически это не образец выбора, а выражение XPath, как мы увидим в главе 7).

    Образцы можно также использовать в атрибуте

    select
    элементов
    <xsl:apply-templates>
    ,
    <xsl:value-of>
    ,
    <xsl:for-each>
    ,
    <xsl:copy-of>
    и
    <xsl:sort>
    ; фактически атрибут
    select
    этих элементов может содержать полные выражения XPath, а не только образцы выбора. Атрибут
    select
    элемента
    <xsl:value-of>
    определяет дочерний узел, значение которого нужно получить:

    <xsl:template match="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:template>

    Теперь пора воспользоваться атрибутом

    select
    элемента
    <xsl:apply-templates>
    , поскольку это даст нам возможность указать, какой шаблон использовать и когда.

    Выбор применяемого шаблона

    Вплоть до нынешнего момента я использовал только версию элемента

    <xsl:apply-templates>
    по умолчанию, как, например:

    <TABLE BORDER="2">

     <TR>

      <TD>Name</TD>

      <TD>Mass</TD>

      <TD>Radius</TD>

      <TD>Day</TD>

     </TR>

     <xsl:apply-templates/>

    </TABLE>

    Простое использование только

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

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

    <MASS>
    ,
    <DAY>
    и
    <RADIUS>
    при помощи
    <xsl:value-of>
    :

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

        </Н1>

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

    </xsl:stylesheet>

    Здесь только извлекается «сырое» строковое значение каждого узла, которое помещается в HTML-таблицу. Однако может потребоваться дополнительная обработка каждого элемента — например, получить также значения атрибутов UNITS каждого элемента и отобразить их:

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

     .

     .

     .

    Для этого нельзя просто применить элемент

    <xsl:value-of>
    , поскольку он вернет значение узла только как текст, а не как значения атрибута. Вместо этого нужно создать новый набор шаблонов, по одному для каждого интересующего нас элемента:
    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    . У элемента
    <NAME>
    нет никаких атрибутов, поэтому для него не нужны шаблоны — нужно только значение узла. Каждому из этих новых шаблонов требуется получить значение элемента, а также значение атрибута
    UNITS
    .

    Чтобы быть уверенным в том, что эти новые шаблоны применяются в правильном порядке, соответствующем заголовкам HTML-таблицы, я явно перечислю все новые шаблоны, выбирая их один за другим при помощи атрибута

    select
    :

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

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

      </TR>

     </xsl:template>

    </xsl:stylesheet>

    ИСПОЛЬЗОВАНИЕ ИМЕНОВАННЫХ ШАБЛОНОВ

    К шаблонам можно также обращаться по имени. Этот вопрос будет рассмотрен в главе 9.

    Теперь к каждому элементу

    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    применяется новый шаблон: мы не просто получаем строковое значение узла при помощи
    <xsl:value-of>
    , мы можем дополнительно обработать каждый элемент — например, прочитать значения атрибута
    UNITS
    . Сначала я получу строковые значения каждого из элементов
    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    . Теперь, когда у нас есть шаблон для каждого из этих узлов-элементов и каждый узел является контекстным узлом в своем шаблоне, вместо того, чтобы ссылаться на узел элемента по имени, мы можем сослаться на него как на контекстный узел при помощи выражения XPath "." (листинг 3.2):

    Листинг 3.2. Версия planets.xsl с выбором

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

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

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

    planets.xsl
    , использующей
    <xsl:value-of select="MASS">
    ,
    <xsl:value-of select="RADIUS">
    и т.д., то есть читающей и отображающей данные каждого элемента
    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    . Теперь же, когда у нас есть отдельный шаблон для каждого из этих элементов, можно осуществить дополнительную обработку — например чтение значений атрибутов.

    Чтение значений атрибутов

    Чтобы получить доступ к значению атрибута при помощи XPath, нужно добавить к имени атрибута префикс @, например: "

    @src
    ", "
    @height
    ", "
    @width
    " и т.д.

    Для выбора любого атрибута можно применить выражение "

    @*
    ". Чтобы сослаться на атрибут
    UNITS
    в каждом элементе
    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    , следует использовать выражение "
    @UNITS
    ". Следовательно, получить значения и отобразить единицы (unit) каждого измерения в
    planets.xml
    можно так (листинг 3.3).

    Листинг 3.3. Чтение значений атрибутов

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

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

      </TR>

     </xsl:template>


     <xsl:template match="MASS">

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

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

     </xsl:template>


     <xsl:template match="RADIUS">

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

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

     </xsl:template>


     <xsl:template match="DAY">

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

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

     </xsl:template>

    </xsl:stylesheet>

    Результаты работы показаны на рис. 3.1, Как видите, теперь мы извлекли строковое значение атрибута UNITS и отобразили его.

    Рис. 3.1. Вывод значений атрибутов, первый вариант


    Рисунок 3.1 не совсем точен: обратите внимание на то, что между значением и соответствующей единицей измерения в таблице нет пробела. Процессор XSLT просто поместил в результирующее дерево текст без каких-либо разделителей между текстовыми узлами. Хотя это в точности соответствует требованиям рекомендации XSLT, нам бы хотелось, чтобы элементы таблицы выглядели как «1516 miles», а не «1516miles». Как нам добавить этот дополнительный пробел?

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

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

    <xsl:text>
    , который используется для вставки символьного текста непосредственно в дерево вывода. У этого элемента только один атрибут:

    • 

    disable-output-escaping
    . Устанавливается в
    yes
    для того, чтобы такие символы, как < и >, выводились буквально, а не как
    &lt;
    и
    &gt;
    . По умолчанию установлен в
    no
    .

    Этот элемент может содержать только текстовый узел.

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

    <xsl:text>
    , позволяющего по ходу дела замещать элементы целиком на текст. Одна из целей применения
    <xsl:text>
    — сохранить символы-разделители, как в следующем примере (листинг 3.4), в котором элемент
    <xsl:text>
    используется для вставки пробелов.

    Листинг 3.4. Вставка пробелов в таблицу стилей

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

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

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

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

    Рис. 3.2. Вывод значений атрибутов, второй вариант 


    Как видите, элемент

    <xsl:text>
    весьма удобен. Тем не менее вам следует знать вот что: по умолчанию элементы
    <xsl:text>
    заменяют символы, которые могут входить в разметку, на эскейп-последовательности. Например,
    <xsl:text>Here is а greater-than sign: ></xsl:text>
    будет выведено как «Here is a greater-than sign: &gt;,», а не как «Herе is a greater-than sign: >». А если попытаться использовать < внутри элемента
    <xsl:text>
    , процессор XSLT посчитает, что вы пытаетесь открыть элемент внутри элемента
    <xsl:text>
    , что неверно. Как же вывести значащие символы, такие как < и >, если есть необходимость? Это можно сделать путем отключения вывода ESC-последовательностей (disabling output escaping).

    УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

    Один из вопросов, которые призван решить XSLT 2.0, это упрощение импорта подобного текста без разбора из других файлов.

    Отключение вывода эскейп-последовательностей

    При помощи

    <xsl:text>
    можно включать в выходной документ непосредственно символы < и &, а не последовательности
    &lt;
    и
    &amp;
    . Для этого следует установить атрибут
    disable-output-escaping
    элемента
    <xsl:text>
    в yes («да», по умолчанию он установлен в
    no
    , «нет»). В приведенном ниже примере я при помощи
    <xsl:text>
    непосредственно пишу текст "
    <PLANET/>
    " в выходной документ:

    <?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:text disable-output-escaping="yes">

       &lt;PLANET/&gt;

      </xsl:text>

     </xsl:template>

    </xsl:stylesheet>

    Результат следующий:

    <HTML>

     <HEAD>

      <TITLE>

       Planets

      </TITLE>

     </HEAD>

     <BODY>

      <PLANET/>

      <PLANET/>

      <PLANET/>

     </BODY>

    </HTML>

    Разумеется, не обязательно выводить

    <PLANET/>
    при помощи
    <xsl:text>
    : этот элемент можно было поместить непосредственно в элемент буквального результата. Но что делать в тех случаях, когда процессор XSLT не сможет распознать нужный нам в выходном документе элемент как фактический элемент? Например, в преобразованных документах XHTML вам потребуется
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
    , но процессор XSLT сочтет, что это не хорошо сформированный XML. Как поместить этот элемент в выходные данные?

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

    <!CDATA[ ]>
    , как мы увидим в главе 6, и попытаться трактовать его как простые символьные данные, но процессоры XSLT все равно заменят < на
    &lt;
    , а > на
    &gt;
    .

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

    <!DOCTYPE>
    — использовать атрибут
    doctype-public
    элемента
    <xsl:output>
    , как мы увидим в главе 6, но в качестве демонстрации для этих целей здесь я отключу вывод ESC-последовательностей в элементе
    <xsl:text>
    (хотя этот способ не рекомендован для создания элементов
    <!DOCTYPE>
    ). Вот как это выглядит:

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

      <xsl:text disab1e-output-escaping="yes">

       &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"&gt;

      </xsl:text>

      <HTML>

       <HEAD>

        <TITLE>

         The Planets Table

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         The Planets Table

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

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

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

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

       .

       .

       .

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

    <xsl:text>
    , в том числе — при обсуждении символов-разделителей далее в этой главе.

    Написание значений атрибутов

    В XSLT есть несколько способов написать значения атрибутов в выходные документы, и наиболее мощный — это создать атрибуты с нуля при помощи элемента

    <xsl:attribute>
    , как мы увидим в главе 6.

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

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

    <MASS>
    ,
    <DAY>
    и
    <NAME>
    , в атрибуты элементов
    <PLANET>
    , преобразуя
    planets.xml
    в следующий вид:

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

    <PLANETS>

     <PLANET DAY="58.65 days" RADIUS="1516 miles"

      MASS=".0553 (Earth = 1)" NAME="Mercury"/>

     <PLANET DAY="116.75 days" RADIUS="3716 miles"

      MASS=".815 (Earth = 1)" NAME="Venus"/>

     <PLANET DAY="1 days" RADIUS="2107 miles"

      MASS="1 (Earth = 1)" NAME="Earth"/>

    </PLANETS>

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

    <NAME>
    ,
    <MASS>
    и
    <DAY>
    и попытался сделать их значениями атрибутов:

    <xsl:template match="PLANET">

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

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

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

    />

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

    Шаблоны значений атрибутов

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

    В этом случае атрибут можно установить в значение выражения XPath (более подробно эта тема изучается в главе 4), если заключить выражение в фигурные скобки. Например, чтобы установить атрибут

    NAME
    в строковое значение элемента
    <DESCRIPTION>
    , дочернего элемента контекстного узла, это значение можно присвоить следующим образом:
    NAME={DESCRIPTION}
    .

    В листинге 3.5 приведен правильный код XSLT, в котором значения элементов

    <NAME>
    ,
    <MASS>
    ,
    <RADIUS>
    и
    <DAY>
    присваиваются атрибутам элемента
    <PLANET>
    с теми же именами.

    Листинг 3.5. Работа с шаблонами значений атрибутов

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

      <xsl:copy>

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

      </xsl:copy>

     </xsl:template>


     <xsl:template match="PLANET">

      <PLANET NAME="{NAME}" MASS="{MASS}" RADIUS="{RADIUS}" DAY="{DAY}"/>

     </xsl:template>

    </xsl:stylesheet>

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

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

    <PLANETS>

     <PLANET DAY="58.65" RADIUS="1516" MASS=".0553" NAME="Mercury"/>

     <PLANET DAY="116.75" RADIUS="3716" MASS=".815" NAME="Venus"/>

     <PLANET DAY="1" RADIUS="2107" MASS="1" NAME="Earth"/>

    </PLANETS>

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

    <MASS>
    ,
    <NAME>
    и
    <RADIUS>
    содержит атрибут
    UNITS
    , задающий единицы измерения, и можно извлечь эти значения. Контекстным узлом является элемент
    <PLANET>
    , поскольку шаблон установлен для выбора этого элемента, поэтому на дочерние элементы
    <MASS>
    ,
    <NAME>
    и
    <RADIUS>
    можно ссылаться как "
    MASS
    ", "
    NAME
    " и "
    RADIUS
    ". Для обращения к атрибуту
    UNITS
    этих элементов можно использовать синтаксис "
    MASS/@UNITS
    ", "
    NAME/@UNITS
    ", и "
    RADIUS/@UNITS
    ":

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

      <xsl:copy>

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

      </xsl:copy>

     </xsl:template>


     <xsl:template match="PLANET">

      <PLANET NAME="{NAME}"

       MASS="{MASS} {MASS/@UNITS}"

       RADIUS="{RADIUS} {RADIUS/@UNITS}"

       DAY="{DAY} {DAY/@UNITS}"/>

     </xsl:template>

    </xsl:stylesheet>

    И вот результат, включающий единицы измерения:

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

    <PLANETS>

     <PLANET DAY="58.65 days" RADIUS="1516 miles"

      MASS=".0553 (Earth = 1)" NAME="Mercury"/>

     <PLANET DAY="116.75 days" RADIUS="3716 miles"

      MASS=".815 (Earth = 1)" NAME="Venus"/>

     <PLANET DAY="1 days" RADIUS="2107 miles"

      MASS="1 (Earth = 1)" NAME="Earth"/>

    </PLANETS>

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

    function printHello {cout << 'Hello';}
    — фигурные скобки необходимо удваивать, для того чтобы процессор XSLT их игнорировал:
    function printHello {{cout<<'Hello';}}
    .

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

    • элементы буквального результата;

    • элементы расширения (см. главу 5);

    • 

    <xsl:attribute>
    . Здесь можно использовать атрибуты
    name
    и
    namespace
    (см. главу 6);

    • 

    <xsl:element>
    . Здесь можно использовать атрибуты
    name
    и
    namespace
    (см. главу 6);

    • 

    <xsl:number>
    . Здесь можно использовать атрибуты
    format
    ,
    lang
    ,
    letter-value
    ,
    grouping-separator
    и
    grouping-size
    (см. главу 4);

    • 

    <xsl:processing-instruction>
    . Здесь можно использовать атрибут
    name
    (см. главу 6);

    • 

    <xsl:sort>
    . Здесь можно использовать атрибуты
    lang
    ,
    data-type
    ,
    order
    и
    case-order
    (см. главу 5).

    В главе 6 эта тема рассмотрена более подробно: мы узнаем, как создавать атрибуты (и новые элементы) с нуля. Дополнительная информация об использовании выражений XPath в шаблонах значений атрибутов приведена в главе 7.

    Обработка символов-разделителей

    Поначалу символы-разделители (whitespace) доставляют авторам XSLT много хлопот. В главе 2 объяснялось, что «чистые» узлы-разделители — это текстовые узлы, содержащие только символы-разделители (пробелы, возвраты каретки, переводы строки и символы табуляции). Эти узлы по умолчанию копируются из исходного документа.

    Заметьте, что в таблице стилей также могут быть узлы-разделители:

    <xsl:template match="PLANETS">

     <xsl:copy>

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

     </xsl:copy>

    </xsl:template>

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

    <TITLE>
    копируется в выходной документ, так как это не чистый узел-разделитель (он также содержит текст «The Planets Table»):

    <xsl:template match="/PLANETS">

     <HTML>

      <HEAD>

       <TITLE>

        The Planets Table

       </TITLE>

       .

       .

       .

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

    <xsl:text>
    , так чтобы символы-разделители стали чистыми узлами-разделителями:

    <xsl:template match="/PLANETS">

     <HTML>

      <HEAD>

       <TITLE>

        <xsl:text/>The Planets Table<xsl:text/>

       </TITLE>

       .

       .

       .

    Чистые узлы-разделители не копируются из таблицы стилей в выходной документ, если только они не находятся внутри элемента

    <xsl:text>
    , или у элемента, в который они вложены, атрибут
    xml:space
    не установлен в «preserve» (сохранить).

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

    <?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:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    и примените ее к

    planets.xml
    ; все символы-разделители будут также скопированы в результирующий документ:

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

     .

     .

     .

    Тем не менее, иногда требуется удалить разделители, используемые при форматировании входных документов — это можно сделать при помощи элемента

    <xsl:strip-space>
    .

    Элементы <xsl:strip-space> и <xsl:preserve-space>

    Элемент

    <xsl:strip-space>
    дает указание процессору XSLT убрать все чистые узлы-разделители (также называемые «потребляемыми», expendable, узлами-разделителями) из. выходного документа. Чистый узел-разделитель состоит только из символов-разделителей и не содержит текст какого-либо другого вида. У этого элемента только один атрибут:

    • 

    elements
    (обязательный). Задает элементы, из которых нужно убрать символы-разделители. Представляет, собой список разделённых символами-разделителями 
    NameTest 
    (именами или обобщёнными именами с символами подстановок).

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

    planets.xml
    , можно применить
    <xsl:strip-space elements="*"/>
    следующим образом:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:strip-space elements="*"/>

     <xsl:output method="xml"/>

     <xsl:template match="*">

      <xsl:copy>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

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

    planets.xml
    . Обратите внимание на то, что убраны все символы-разделители, в том числе все символы новой строки:

    <?xml version="1.0" encoding="utf-8"?><PLANETS><PLANET><NAME>Mercury</NAME><MASS>.0553</MASS><DAY>58.65</DAY><RADIUS>1516</RADIUS><DENSITY>.983</DENSITY><DISTANCE>43.4</DISTANCE></PLANET><PLANET><NAME>Venus</NAME><MASS>.815</MASS><DAY>116.75</DAY><RADIUS>3716</RADIUS><DENSITY>.943</DENSITY><DISTANCE>66.8</DISTANCE></PLANET><PLANET><NAME>Earth</NAME><MASS>1</MASS><DAY>1</DAY><RADIUS>2107</RADIUS><DENSITY>1</DENSITY><DISTANCE>128.4</DISTANCE></PLANET></PLANETS>

    Заметьте, что таким образом удаляются только чистые узлы-разделители. Например, текст элемента

    <TITLE>Volcanoes for Dinner</TITLE>
    не содержит чистых текстовых узлов-разделителей, поэтому текст «Volcanoes for Dinner» будет сохранен в выходном документе вместе с пробелами. Так будет даже тогда, когда текст будет содержать несколько пробелов подряд, как в «Volcanoes for Dinner».

    В некоторых ситуациях может потребоваться не удалять все узлы-разделители из всего документа; задать элементы, в которых следует сохранить узлы-разделители, можно при помощи элемента

    <xsl:preserve-space>
    . У этого элемента такой же атрибут, что и у
    <xsl:strip-space>
    :

    • 

    elements
    (обязательный). Задает элементы, в которых нужно сохранить символы-разделители. Представляет собой список разделенных символами-разделителями
    NameTest
    (именами или обобщенными именами с символами подстановок).

    Фактически элемент

    <xsl:preserve-space>
    является элементом по умолчанию для всех элементов в XSLT. Если вы использовали элемент
    <xsl:strip-space>
    , все равно можно указать, в каком элементе или элементах нужно сохранить узлы-разделители, установив атрибут
    elements
    элемента
    <xsl:preserve-space>
    в список этих элементов:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:strip-space elements="*"/>

     <xsl:preserve-space elements="MASS RADIUS"/>

     <xsl:output method="xml"/>

     <xsl:template match="*">

      <xsl:copy>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

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

    indent
    элемента
    <xsl:output>
    позволяет автоматически выровнять выходной документ.

    Автоматическое выравнивание

    Элемент

    <xsl:output>
    поддерживает атрибут
    indent
    который устанавливается в «yes» или «no», и указывает процессору XSLT, нужно ли выравнивать результирующий документ. Как правило, выравнивание результирующего документа не имеет большого значения, поскольку с ним работает приложение, которому все равно, выровнен документ или нет, как мы видели в примерах преобразований XML- XML и XML-HTML. Однако иногда требуется представить результирующий документ в виде простого текста, и в таких случаях выравнивание документа для отображения иерархической структуры может оказаться удобным.

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

    planets.xml
    без какого-либо выравнивания:

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

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

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

    <xsl:output indent="yes"/>
    можно указать процессору XSLT осуществлять выравнивание документа при преобразовании его в HTML (листинг 3.6).

    Листинг 3.6. Таблица стилей, задающая выравнивание

    <?xml version="1.0"?>

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

    <xsl:output indent="yes"/>

    <xsl:template match="/PLANETS">

    <HTML>

    <HEAD>

    <TITLE<

    The Planets Table

    </TITLE></HEAD> <BODY> <H1>

    The Planets Table

    </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:value-of select="MASS"/></TD>

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

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

    </TR>

    </xsl:template>

    </xsl:stylesheet>

    Результат применения таблицы с использованием процессора Saxon (в котором особенно хорошо реализовано выравнивание) с требуемым выравниванием:

    <HTML>

     <HEAD>

      <meta http-equiv="Content-Type" content="text/html" charset="utf-8">

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TD>Name</TD>

       <TD>Mass</TD>

       <TD>Radius</TD>

       <TD>Day</TD>

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

       <TR>

        <TD>Earth</TD>

        <TD>1</TD>

        <TD>2107</TD>

        <TD>1</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Как видите, в XSLT обработке символов-разделителей приходится уделять достаточное внимание, но процедура упрощается, если вы знаете, что происходит.

    ВЫРАВНИВАНИЕ ДОКУМЕНТОВ В ЭТОЙ КНИГЕ

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

    Правила по умолчанию в шаблоне

    Взгляните на следующую таблицу стилей XSLT — в ней заданы правила для выбора корневого узла, узлов

    <PLANETS>
    и узлов
    <PLANET>
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/">

      <HTML>

       <xsl:apply-templates/>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANETS">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANET">

      <P>

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

      </P>

     </xsl:template>

    </xsl:stylesheet>

    Обратите внимание на правило для элемента

    <PLANETS>
    : в нем просто используется элемент
    <xsl:apply-templates>
    для применения шаблонов ко всем дочерним узлам. Однако при обработке шаблона существует правило по умолчанию: если для элемента не задано правило, автоматически вызывается
    <apply-templates/>
    . Таким образом, следующая таблица стилей, в которой опущено правило для
    <PLANETS>
    , делает в точности то же, что и предыдущая:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/">

      <HTML>

       <xsl:apply-templates/>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <P>

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

      </P>

     </xsl:template>

    </xsl:stylesheet>

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

    • Корневой узел. По умолчанию вызывается

    <xsl:apply-templates/>
    ;

    • Узлы элементов. По умолчанию вызывается

    <xsl:apply-templates/>
    ;

    • Узлы атрибутов. Копирует в результирующий документ значение атрибута, однако копирует его как текст, но не как атрибут;

    • Текстовые узлы. Копирует в результирующий документ текст;

    • Узлы комментариев. Нет обработки XSLT, ничего не копируется;

    • Узлы инструкций обработки. Нет обработки XSLT, ничего не копируется;

    • Узлы пространств имен. Нет обработки XSLT, ничего не копируется.

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

    <xsl:template match="*">

     <xsl:apply-templates/>

    </xsl:template>

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

    Правило по умолчанию для текстовых узлов можно выразить следующим образом: функция XSLT

    text
    выбирает текст узла, так что текст текстового узла добавляется в выходной документ:

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

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

    </xsl:template>

    Правило по умолчанию того же вида применяется к атрибутам, которые добавляются в выходной документ при помощи следующего правила, где выражение "@*" выбирает любой атрибут:

    <xsl:template match="@*">

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

    </xsl:template>

    По умолчанию инструкции обработки не вставляются в выходной документ, поэтому их правило по умолчанию можно выразить просто при помощи следующей функции-инструкции обработки XSLT, которая выбирает инструкции обработки (как мы увидим в главе 8):

    <xsl:template match="processing-instruction()"/>

    То же верно для комментариев — их правило по умолчанию может быть выражено при помощи функции XSLT

    comment
    , которая также будет рассмотрена в главе 8:

    <xsl:template match="comment()"/>

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

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

    </xsl:stylesheet>

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

    planets.xml
    . Заметьте, что правило по умолчанию для атрибутов не применялось, потому что они не являются дочерними по отношению к другим узлам:

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


    Mercury

    .0553

    58.65

    1516

    .983

    43.4


    Venus

    .815

    116.75

    3716

    .943

    66.8


    Earth

    1

    1

    2107

    1

    128.4

    ПРАВИЛА ПО УМОЛЧАНИЮ И INTERNET EXPLORER

    Одна из проблем при работе с XSLT в Internet Explorer 5.5 или младше состоит в том, что браузер не предоставляет никаких правил по умолчанию. Необходимо задавать все правила самостоятельно, если только не установлен процессор MSXML3 в режиме замены (подробнее см. главу 2) или вы не обновили браузер до Internet Explorer 6.0.

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

    <xsl:output preserve-space="*"/>
    .

    Удаление содержимого

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

    Предположим, нам нужно удалить из

    planets.xml
    все данные о планетах, за исключением их названий и данных о массе. Следующая таблица стилей выполняет данную задачу.

    Листинг 3.7. Удаление содержимого

    <?xml versiоn="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:strip-space elements="*"/>

     <xsl:output method="xml" indent="yes"/>

     <xsl:template match="/">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANETS">

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:copy>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>


     <xsl:template match="NAME">

      <xsl:copy>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>


     <xsl:template match="MASS">

      <xsl:copy>

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

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

      </xsl:copy>

     </xsl:template>


     <xsl:template match="RADIUS">

     </xsl:template>


     <xsl:template match="DAY">

     </xsl:template>


     <xsl:template match="DENSITY">

     </xsl:template>


     <xsl:template match="DISTANCE">

     </xsl:template>

    </xsl:stylesheet>

    А вот результирующий документ (отметьте, что я сохранил только элементы

    <NAME>
    и
    <MASS>
    ):

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

    <PLANET>

     <NAME>Mercury</NAME>

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

    </PLANET>

    <PLANET>

     <NAME>Venus</NAME>

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

    </PLANET>

    <PLANET>

     <NAME>Earth</NAME>

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

    </PLANET>

    Таким способом можно фильтровать XML-документы, создавая новые XML-документы только с требуемыми данными.

    Разрешение конфликтов шаблонов

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

    У каждого шаблона есть приоритет по умолчанию, основанный на значении атрибута

    select
    . Как правило, чем более сужающим является правило выбора или выражение (например, "
    PLANET
    " и "
    *
    "), тем выше его приоритет. В главе 4 мы рассмотрим, как процессор определяет приоритеты и как он работает с шаблонами, имеющими одинаковый приоритет.

    Приоритет шаблона можно установить при помощи атрибута

    priority
    . В приведенном ниже листинге 3.8 правило, созданное элементом
    <xsl:template priority="1"/>
    , имеет меньший приоритет, чем правило, созданное элементом
    <xsl:template priority="2"/>
    .

    Листинг 3.8. Установка приоритета шаблона

    <?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: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" priority="2">

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

      (<I>Very</I> heavy)

     </xsl:template>


     <xsl:template match="MASS" priority="1">

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

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

    Процессор XSLT выбирает шаблон с наивысшим приоритетом, который добавляет текст "

    (<I>Very</I> heavy)
    " после каждого значения массы. В следующем примере использован шаблон с наивысшим приоритетом:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </Н1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>.0553(<I>Very</I> heavy)</TD>

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>.815(<I>Very</I> heavy)</TD>

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

        <TD>1(<I>Very</I> heavy)</TD>

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    УСОВЕРШЕНСТВОВАНИЯ В XSLT 2.0

    Вопрос приоритета шаблонов должен быть учтен в XSLT 2.0. В частности, W3C рассматривает возможность добавления нового элемента с предварительным названием <xsl:next-match/>, который позволит выбирать для шаблона второй лучше всего подходящий элемент.

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

    PLANET
    " будет отдано предпочтение перед обобщенным условием "
    *
    ".

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

    Элемент

    <xsl:copy>
    позволяет скопировать узел из исходного дерева в выходное. Заметьте, однако, что это поверхностное (shallow) копирование, при котором не копируются потомки и атрибуты узла. У элемента есть один атрибут:

    • 

    use-attribute-sets
    . Задает названия наборов атрибутов, которые нужно применить к создаваемому элементу. Принимает значение списка
    QName
    , разделенных символами-разделителями. Этот атрибут можно использовать только в том случае, когда контекстный узел является элементом. Дополнительная информация о наборах атрибутов приведена в главе 6. 

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

    <xsl:copy>
    не задействован, поскольку узел выходного документа создается автоматически.

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

    Листинг 3.9. Таблица стилей, копирующая элементы

    <?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:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Однако

    <xsl:copy>
    не копирует атрибуты — вот результат применения этой таблицы стилей к
    planets.xml
    :

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

    <PLANETS>

     <PLANET>

      <NAME>Mercury</NAME>

      <MASS>.0553</MASS>

      <DAY>58.65</DAY>

      <RADIUS>1516</RADIUS>

      <DENSITY>.983</DENSITY>

      <DISTANCE>43.4</DISTANCE>

     </PLANET>

     <PLANET>

      <NAME>Venus</NAME>

      <MASS>.815</MASS>

      <DAY>116.75</DAY>

      <RADIUS>3716</RADIUS>

      <DENSITY>.943</DENSITY>

      <DISTANCE>66.8</DISTANCE>

     </PLANET>

     <PLANET>

      <NAME>Earth</NAME>

      <MASS>1</MASS>

      <DAY>1</DAY>

      <RADIUS>2107</RADIUS>

      <DENSITY>1</DENSITY>

      <DISTANCE>128.4</DISTANCE>

     </PLANET>

    </PLANETS>

    Копирование атрибутов несколько сложнее, потому что нужно найти какой-либо способ применить

    <xsl:copy>
    к каждому атрибуту элемента. Это можно сделать, например, при помощи элемента
    <xsl:for-each>
    , о котором пойдет речь в главе 5.

    Листинг 3.10. Копирование атрибутов

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

    А вот результат — заметьте, что на этот раз атрибуты не затронуты:

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

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

     </PLANET>

    </PLANETS>

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

    <xsl:copy>
    можно применить
    <xsl:copy-of>
    .

    ГЛУБОКОЕ КОПИРОВАНИЕ

    Пример использования <xsl:copy> для осуществления глубокого копирования документа будет приведен в главе 4, в которой описывается функция узла и объясняется, как рекурсивно вызывать один и тот же шаблон.

    Элемент <xsl:copy-of>

    Элемент

    <xsl:copy-of>
    позволяет осуществлять глубокое копирование узлов, при котором копируется не только узел, но и все его атрибуты и потомки. У этого элемента единственный атрибут:

    • 

    select
    (обязательный). Узел или набор копируемых узлов. Этот элемент пуст и не имеет содержимого.

    Ниже приведен пример работы этого элемента; в этом случае я заменил в листинге 3.10 элемент

    <xsl:for-each>
    элементом
    <xsl:copy-of>
    , который явно выбирает для копирования все атрибуты контекстного элемента.

    Листинг 3.11. Применение <copy-of>

    <?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:copy-of select="@*"/>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>

    </xsl:stylesheet>

    Этот код работает так же, как и предыдущий пример, копируя все элементы и атрибуты. С другой стороны, можно вообще обойтись без каких-либо изменений в листинге 3.10, — я могу просто использовать

    <xsl:copy-of>
    для того, чтобы скопировать весь документ, выбрав корневой узел и скопировав всех его потомков:

    <?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-of select="*"/>

     </xsl:template>

    </xsl:stylesheet>

    При помощи

    <xsl:copy-of>
    можно также копировать конкретные узлы и их потомков вместо того, чтобы задавать подстановку *. Например, следующее правило копирует все элементы
    <MASS>
    и их потомков:

    <xsl:template match="MASS">

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

    </xsl:template>

    По этой причине я могу заменить элемент

    <MASS>
    на элемент
    <DAY>
    :

    <xsl:template match="MASS">

     <xsl:copy-of select="DAY"/>

    </xsl:template>

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

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

    <xsl:message>
    можно дать указание процессору XSLT отобразить сообщение и, по выбору, прекратить обработку таблицы стилей. У элемента
    <xsl:message>
    один атрибут:

    • 

    terminate
    (необязательный). Значение «yes» прекращает обработку. По умолчанию установлено «no».

    Куда на самом деле будет отправлено сообщение, зависит от процессора XSLT. Для процессоров, основанных на Java, сообщение обычно отправляется в выходной поток ошибок Java, которому соответствует экран компьютера, если процессор XSLT был вызван из командной строки. Другие процессоры XSLT могут выводить сообщения во всплывающие окна или в web-страницы, отправляемые в браузеры.

    В приведенном ниже листинге 3.12 я прекращаю обработку XSLT, когда процессор XSLT пытается преобразовать элемент

    <DAY>
    в
    planets.xml
    , выводя сообщение "
    Sorry, DAY information is classified.
    " (Извините, информация о параметре «ДЕНЬ» засекречена.).

    Листинг 3.12. Применение <xsl:message>

    <?xml version="1.0"?>

    <xsl:stylesheet version="l.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">

         <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: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:message terminate="yes">

       Sorry. DAY information is classified.

      </xsl:message>

     </xsl:template>

    </xsl:stylesheet>

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

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

    file:///C:/XSL/messages/message.xsl: Line 49; Column 38;

    Sorry. DAY information is classified.

    XSLT Error (javax.xml.transform.TransformerException):

    Stylesheet directed termination

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

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

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







     


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