• Функции XSLT
  • current()
  • document()
  • element-available()
  • format-number()
  • function-available()
  • generate-id()
  • key()
  • system-property()
  • unparsed-entity-uri()
  • Функции XPath для работы с наборами узлов
  • count()
  • id()
  • last() 
  • local-name()
  • name() 
  • namespace-uri()
  • position()
  • Функции XPath для работы со строками
  • concat()
  • contains()
  • normalize-space()
  • starts-with()
  • string()
  • string-length()
  • substring()
  • substring-after()
  • substring-before()
  • translate()
  • Функции XPath для работы с числами
  • ceiling()
  • floor()
  • number()
  • round()
  • sum()
  • Логические функции XPath
  • boolean()
  • false()
  • lang() 
  • not()
  • true()
  • Элемент <xsl:decimal-format>: создание числовых форматов
  • Новые функции в XSLT 2.0 и XPath 2.0
  • Глава 8

    Работа с функциями XSLT и XPath

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

    id
    ,
    generate-id
    ,
    position
    ,
    count
    и т.д). В этой главе мы рассмотрим их все.

    Некоторые функции входят в XSLT, некоторые пришли из XPath. И XSLT, и XPath обновляются до версии 2.0, и в конце главы я приведу список новых, готовящихся к выпуску функций. (Пока что никаких новых функций версии 2.0 еще не опубликовано.)

    В XSLT встроены следующие функции:

    element-available()
    ;

    function-available()
    ;

    current()
    ;

    document()
    ;

    key()
    ;

    format-number()
    ;

    generate-id()
    ;

    system-property()
    ;

    unparsed-entity-uri()
    .

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

    count()
    ;

    id()
    ;

    last()
    ;

    local-name()
    ;

    name()
    ;

    namespace-uri()
    ;

    position()
    .

    Следующие функции XPath работают со строками:

    concat()
    ;

    contains()
    ;

    normalize-space()
    ;

    starts-with()
    ;

    string()
    ;

    string-length()
    ;

    substring()
    ;

    substring-after()
    ;

    substring-before()
    ;

    translate()
    .

    Для работы с числами служат следующие функции XPath:

    ceiling()
    ;

    floor()
    ;

    number()
    ;

    round()
    ;

    sum()
    .

    И эти функции XPath работают с логическими значениями:

    boolean()
    ;

    false()
    ;

    lang()
    ;

    not()
    ;

    true()
    .

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

    Функции XSLT

    В следующем списке перечислены встроенные в XSLT функции:

    • 

    current()
    . Возвращает текущий (current) узел — но не контекстный узел. Текущий узел — это узел, используемый в данный момент в таких циклах, как
    <xsl:for-each>
    . Функцию current нельзя применять в образцах;

    • 

    document()
    . Позволяет считывать несколько документов;

    • 

    element-available()
    . Показывает, доступен ли элемент расширения;

    • 

    format-number()
    . Форматирует числа при выводе;

    • 

    function-available()
    . Показывает, доступна ли функция расширения;

    • 

    generate-id()
    . Указывает процессору XSLT присвоить идентификатор узлу и возвратить его. При повторном применении generate-id к тому же узлу функция возвращает присвоенный ранее идентификатор;

    • 

    key()
    . Позволяет осуществлять поиск по ключу;

    • 

    system-property()
    . Позволяет проверить три системных свойства:
    xsl:version
    (версия XSLT, поддерживаемая процессором XSLT),
    xsl:vendor
    (производитель процессора XSLT) и
    xsl:vendor-url
    (URL производителя процессора XSLT);

    • 

    unparsed-entity-uri()
    . Предоставляет доступ к неразобранным сущностям, объявленным в DTD или схеме, через URI.

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

    current()

    Функция

    current
    возвращает текущий (current) — не контекстный (context) — узел. Контекстный узел шаблона — это узел в выбранном наборе узлов, к которому применяется шаблон. Текущий узел, с другой стороны, это используемый в данный момент узел в таких циклах, как
    <xsl:for-each>
    . Функция возвращает текущий узел как набор узлов с одним узлом:

    node-set current()

    Обратите внимание:

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

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

    <PLANET>
    при помощи шаблона (листинг 8.1), демонстрирует работу функции
    current
    . Внутрь шаблона я поместил элемент
    <xsl:for-each>
    , и шаблон применяется только в том случае, если выбранный шаблоном контекстный узел в то же время является и текущим узлом в очередной итерации в элементе
    <xsl:for-each>
    .

    Листинг 8.1. Применение функции current

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

        <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:for-each select="/PLANETS/*[.=current()]">

       <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:for-each>

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

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

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

    document()

    Функция

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

    node-set document(uri, base-uri?)

    Функция принимает параметр

    uri
    , который может представлять собой URI читаемого документа или набор узлов, чьи строковые значения задают идентификаторы URI. Второй, необязательный параметр
    base-uri
    представляет собой набор узлов, базовый URI которого используется для вычисления всех относительных URI, могущих находиться в параметре
    uri
    .

    В следующем примере (листинг 8.2) я обрабатываю в процессоре XSLT один документ,

    planets1.xml
    , и также считываю и обрабатываю второй документ,
    planets2.xml
    . Вот
    planets1.xml
    .

    Листинг 8.2. planets1.xml

    <?xml version="1.0"?>

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

    <PLANETS href="planets2.xml">

    </PLANETS>

    Вот

    planets2.xml
    — в элементе
    <PLANET>
    содержится одна планета (листинг 8.3).

    Листинг 8.3. planets2.xml

    <?xml version="1.0"?>

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

    А вот таблица стилей

    planets.xml
    , которую я применяю к
    planets1.xml
    . Таблица стилей содержит шаблон, выбирающий элемент
    <PLANET>
    в
    planets1.xml
    , и в этом шаблоне я считываю
    planets2.xml
    при помощи
    <xsl:apply-templates>
    и функции
    document
    (листинг 8.4).

    Листинг 8.4. Применение функции document

    <?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 select="document(@href)"/>

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

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

      <xsl:text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

    Эта таблица стилей обрабатывает данные в

    planets1.xml
    и также считывает и обрабатывает
    planets2.xml
    ; вот полный результат — как видите, данные для
    planets2.xml
    добавились нужным образом:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Функция

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

    element-available()

    Функция

    element-available()
    используются для определения доступности определенного элемента расширения. Ее применяют следующим образом:

    boolean element-available(element-name)

    Функция принимает имя искомого элемента и, если элемент доступен, возвращает истину, если недоступен — ложь.

    Мы уже встречались с этой функцией в главе 5. В примере

    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>

    format-number()

    Как можно догадаться по имени, функция

    format-number()
    служит для форматирования чисел с преобразованием их в строки. Она применяется так:

    string format-number(number, format, name?)

    Функция возвращает форматируемое число в виде строки. Функции передаются число

    number
    , которое нужно отформатировать, строка форматирования
    format
    и необязательный параметр-строка
    name
    . Строка name — это имя
    QName
    , задающее формат так, как он создается элементом
    <xsl:decimal-format>
    (который будет рассмотрен в конце этой главы).

    Форматирующая строка

    format
    должна соответствовать соглашениям класса Java
    DecimalFormat
    .

    КЛАСС JAVA DECIMALFORMAT

    На момент написания книги документация для класса Java DecimalFormat находится в Интернете по адресу: http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html.

    Форматирующая строка (format string) состоит из следующих частей:

    • 

    format-string:= subpattern (;subpattern)?

    • 

    subpattern:= prefix? integer (.fraction)?suffix?

    • 

    prefix:= [#x0..#xFFFD] - specialCharacters

    • 

    suffix:= [#x0..#xFFFD] - specialCharacters

    • 

    integer:= '#'* '0'* '0'

    • 

    fraction:= '0'* '#'*

    Далее показаны специальные символы (

    specialCharacters
    ), которые можно использовать в подчиненных образцах,
    subpattern
    (эти символы можно изменить при помощи элемента
    <xsl:decimal-format>
    , который будет рассмотрен в конце этой главы):

    • 

    0
    — на этом месте всегда должна стоять цифра;

    • 

    #
    — цифра, если только это не избыточный лидирующий или завершающий ноль;

    • 

    .
    — разделитель десятичной части;

    • 

    ,
    — разделитель групп разрядов;

    • 

    ;
    — разделяет форматы;

    • 

    -
    — знак минуса;

    • 

    %
    — умножить на 100 и показать как проценты;

    • 

    — умножить на 1000 и показать в тысячных частях;

    • 

    Е
    — разделяет мантиссу и экспоненциальную часть;

    • 

    ¤
    — символ валюты (#xA4);

    • 

    '
    — заключает в кавычки специальные символы.

    Следующий пример (листинг 8.5) демонстрирует работу функции. Я форматирую значения из

    planets.xml
    , отображаемые в таблице HTML.

    Листинг 8.5. Форматирование чисел

    <?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: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="format-number(., '#.###')"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>


     <xsl:template match="RADIUS">

      <xsl:value-of select="format-number(., '#.###')"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>


     <xsl:template match="DAY">

      <xsl:value-of select="format-number(., '###.#')"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

    Вот результат, в котором выведены отформатированные числа:

    <HTML>

     <HEAD>

      <TITLE>

       The Formatted Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Formatted Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

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

        <TD>1.516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

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

        <TD>3.716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2.107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    Этот результирующий документ показан на рис. 8.1. (MSXML3 и Saxon отбрасывают лидирующие нули, поэтому 0.055 выводится как .055 и т.д.)

    Рис. 8.1. Форматирование чисел при помощи XSLT 


    Следующие примеры демонстрируют способы применения форматирующих строк. Заметьте, что при помощи точки с запятой (;) можно отделять образцы для положительных и отрицательных чисел.

    Число Форматирующая строка Результат
    4567 #,### 4,567
    4567.8 ####.# 4567.8
    4567.8 #,##0.00 4,567.80
    456.789 #,##0.00 456.79
    4567890 #,##0.00 4,567,890.00
    4567 ###0.0### 4567.0
    .00045 ##0.0### 0.0005
    .45 #00% 45%
    -4.56 #.00;(#.00) (4.56)
    -45 #,##0.00 -45

    function-available()

    При помощи функции XSLT 1.0

    function-available
    можно проверить наличие функции расширения:

    boolean function-available(function-name)

    Функция принимает в качестве параметра имя искомой функции и, если функция доступна, возвращает истину, если нет — ложь.

    Следующий пример мы уже рассматривали в главе 5. В этом случае я хочу выполнить ряд математических операций при помощи функции расширения

    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>

    generate-id()

    Функция

    generate-id
    дает указание процессору XSLT присвоить узлу идентификатор (который возвращается как строка). Функция применяется следующим образом:

    string generate-id(node)

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

    Следующий пример взят из главы 6, но сейчас я собираюсь добавить в

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

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

         <H2><A HREF="#{generate-id()}">

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

         </A></H2>

         <P/>

        </xsl:for-each>

        .

        .

        .

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

    <NAME>
    закладки в идентификатор для каждого элемента
    <PLANET>
    так, чтобы он стал гиперссылкой-назначением:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

       <TD><A NAME="{generate-id(.)}">

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

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

    Вот и все; сейчас я создал гиперссылки с атрибутом

    HREF
    , значение которого равно идентификатору элемента
    <PLANET>
    ; при помощи же этого идентификатора я сделал каждый элемент
    <PLANET>
    назначением гиперссылки.

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

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <H2>

       <A href="#N5">Mercury</A>

      </H2>

      <P></P>

      <H2>

       <A href="#N20">Venus</A>

      </H2>

      <P></P>

      <H2>

       <A href="#N3B">Earth</A>

      </H2>

      <P></P>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD><A NAME="N5">Mercury</A></TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD><A NAME="N20">Venus</A></TD>

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

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD><A NAME="N3B">Earth</A></TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    key()

    Функция

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

    node-set key(name, value)

    Функции передаются имя ключа

    name
    в виде строки и требуемое значение ключа
    value
    , которое нужно найти. Функция возвращает набор узлов из найденных узлов.

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

    <xsl:key>
    . Мы уже видели следующий пример в главе 4 — здесь я при помощи ключей выбираю планеты, чей атрибут
    COLOR
    установлен в «
    BLUE
    »:

    <?xml version="1.0"?>

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

    <PLANETS>

     .

     .

     .

     <PLANET COLOR="BLUE">

      <NAME>Earth</NAME>

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

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

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

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

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

     </PLANET>

    </PLANETS>

    Теперь при помощи

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

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

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

     .

     .

     .

    Теперь я могу применить образец «

    key()
    » для выбора элементов
    <PLANET>
    с атрибутом
    COLOR
    со значением «
    BLUE
    » таким образом:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

        <TABLE BORDER="2">

         <TR>

          <TD>Name</TD>

          <TD>Mass</TD>

          <TD>Radius</TD>

          <TD>Day</TD>

         </TR>

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

        </TABLE>

       </BODY>

      </HTML>

     </xsl:template>

     .

     .

     .

    И вот результат — как видите, единственной планетой, удовлетворившей образцу, была Земля:

    <HTML>

     <НЕАD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    system-property()

    Функция

    system-property
    возвращает значение нескольких системных свойств в виде строк; вот как ей следует пользоваться:

    string system-property(property)

    Можно проверять следующие возможные значения системного свойства

    property
    :

    xsl:version
    . Возвращает версию XSLT;

    xsl:vendor
    . Возвращает строку, идентифицирующую производителя процессора XSLT;

    xsl:vendor-url
    . Возвращает URL производителя процессора XSLT.

    Вот пример — версию XSLT можно проверить, вызвав

    system-property('xsl:version')
    ;

    <?xml version="1.0"?>

    <xsl:stylesheet version="2.0"

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

     .

     .

     .

     <xsl:if text="system-property('xsl:version')=2.0">

      <xsl:namespace name="starpowder"/>

     </xsl:if>

     .

     .

     .

    </xsl:stylesheet>

    Проверка версии XSLT может понадобиться, если вы хотите воспользоваться возможностями, которые появились в последних версиях.

    unparsed-entity-uri()

    Функция

    unparsed-entity-uri()
    предоставляет доступ к объявлениям неразобранных сущностей в DTD или схеме исходного документа. Неразобранная сущность (unparsed entity) — это обычно двоичные данные, такие как файл с рисунком. Функция применяется следующим образом:

    string unparsed-entity-uri(name)

    В функцию передается имя name неразобранной сущности, и функция возвращает для нее идентификатор. В следующем примере (листинг 8.6) я добавил в

    planets.xml
    DTD и объявил три неразобранных сущности, соответствующих рисункам планет —
    image1
    ,
    image2
    и
    image3
    , — и обратился к ним, добавив в каждый элемент
    <PLANET>
    атрибут
    IMAGE
    .

    Листинг 8.6. planets.xml с неразобранными сущностями

    <?xml version="1.0"?>

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

    <!DOCTYPE PLANETS [

    <!ELEMENT PLANET (CUSTOMER)*>

    <!ELEMENT CUSTOMER (NAME,MASS,RADIUS,DAY)>

    <!ELEMENT NAME (#PCDATA)>

    <!ELEMENT MASS (#PCDATA)>

    <!ELEMENT RADIUS (#PCDATA)>

    <!ELEMENT DAY (#PCDATA)>

    <!ENTITY image1 SYSTEM "http://starpowder.com/image1.gif" NDATA GIF>

    <!ENTITY image2 SYSTEM "http://starpowder.com/image2.gif" NDATA GIF>

    <!ENTITY image3 SYSTEM "http://starpowder.com/image3.gif" NDATA GIF>

    ]>

    <PLANETS>

     <PLANET IMAGE="image1">

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

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

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

    (Заметьте, что некоторые процессоры XSLT основаны на разборщиках XML, которые проверяют, могут ли они найти сущность по заданному URI, — потому, если вы хотите опробовать этот пример, вставьте URI каких-либо настоящих рисунков.) Теперь я могу извлечь URI рисунков в таблице стилей при помощи функции

    unparsed-entity-uri
    и создать элементы HTML
    <IMG>
    , применив эту функцию в результирующем документе (листинг 8.7).

    Листинг 8.7. Применение unparsed-entity-uri

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <TR>

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

        <IMG SRC="{unparsed-entity-uri(@IMAGE)}"/>

       </TD>

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

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

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

      </TR>

     </xsl:template>


     <xsl:template match="MASS">

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

      <xsl:text> </xsl:text>

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

     </xsl:template>

     .

     .

     .

     <xsl:template match="DAY">

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

      <xsl:text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury<IMG SRC="http://starpowder.com/image1.gif"></TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       <TR>

        <TD>Venus<IMG SRC="http://starpowder.com/image2.gif"></TD>

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

        <TD>3716 miles</TD>

        <TD>116.75 days</TD>

       </TR>

       <TR>

        <TD>Earth<IMG SRC="http://starpowder.com/image3.gif"></TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

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

    Функции XPath для работы с наборами узлов

    Следующие функции XPath работают с наборами узлов:

    • 

    count(node-set)
    . Возвращает число узлов в наборе узлов;

    • 

    id(string ID)
    . Возвращает набор узлов элемента, ID которого совпадает с переданной функции строкой, или пустой набор узлов, если таковых элементов не оказалось;

    • 

    last()
    . Возвращает число узлов в наборе узлов;

    • 

    local-name(node-set)
    . Возвращает локальное имя первого узла в наборе узлов;

    • 

    name(node-set)
    . Возвращает полное имя первого узла в наборе узлов;

    • 

    namespace-uri(node-set)
    . Возвращает URI пространства имен первого узла в наборе узлов;

    • 

    position()
    . Возвращает позицию контекстного узла в контекстном наборе узлов, начиная с 1.

    count()

    Функция

    count
    подсчитывает число узлов в наборе узлов

    number count(node-set)

    Функция принимает набор узлов и возвращает количество узлов в этом наборе. Следующий пример применения функции

    count
    мы уже рассматривали в главе 6. В этом случае набор узлов образован из всех элементов
    <PLANET>
    в
    planets.xml
    ; я получил его при помощи пути расположения «
    //PLANET
    »:

    <xsl:stylesheet

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

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

     <xsl:template match="*">

      <xsl:copy>

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:copy use-attribute-sets="numbering">

       <xsl:apply-templates/>

      </xsl:copy>

     </xsl:template>


     <xsl:attribute-set name="numbering">

      <xsl:attribute name="number"><xsl:number/></xsl:attribute>

      <xsl:attribute name="total">

       <xsl:value-of select="count(//PLANET)"/>

      </xsl:attribute>

     </xsl:attribute-set>

    </xsl:stylesheet>

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

    <PLANET>
    обладает и атрибутом
    number
    , и атрибутом
    total
    , а атрибут
    total
    хранит общее количество элементов
    <PLANET>
    , которое было найдено при помощи
    count
    :

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

    <PLANETS>

     <PLANET number="1" total="3">

      <NAME>Mercury</NAME>

      <MASS>.0553</MASS>

      <DAY>58.65</DAY>

      <RADIUS>1516</RADIUS>

      <DENSITY>.983</DENSITY>

      <DISTANCE>43.4</DISTANCE>

     </PLANET>

     <PLANET number="2" total="3">

      <NAME>Venus</NAME>

      <MASS>.815</MASS>

      <DAY>116.75</DAY>

      <RADIUS>3716</RADIUS>

      <DENSITY>.943</DENSITY>

      <DISTANCE>66.8</DISTANCE>

     </PLANET>

     <PLANET number="3" total="3">

      <NAME>Earth</NAME>

      <MASS>1</MASS>

      <DAY>1</DAY>

      <RADIUS>2107</RADIUS>

      <DENSITY>1</DENSITY>

      <DISTANCE>128.4</DISTANCE>

     </PLANET>

    </PLANETS>

    id()

    Функция id возвращает набор узлов, в котором все узлы имеют такой же ID, как и переданный в эту функцию. Функция применяется следующим образом:

    node-set id(id-value)

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

    Следующее правило-пример выбирает текст всех элементов с ID «favorite»:

    <xsl:template match = "id('favorite')">

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

    </xsl:template>

    Отметьте также, что вы должны объявлять значения ID как в DTD или схеме. Объявление DTD для

    planets.xml
    , объявляющее ID с именем
    id
    и присваивающее ему значение «favorite», может выглядеть так:

    <?xml version="1.0"?>

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

    <!DOCTYPE PLANETS [

    <!ELEMENT PLANET (CUSTOMER)*>

    <!ELEMENT CUSTOMER (NAME,MASS,RADIUS,DAY)>

    <!ELEMENT NAME (#PCDATA)>

    <!ELEMENT MASS (#PCDATA)>

    <!ELEMENT RADIUS (#PCDATA)>

    <!ELEMENT DAY (#PCDATA)>

    <!ATTLIST PLANET

     id ID #REQUIRED>

    ]>

    <PLANETS>

     <PLANET id='favorite'>

      <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 не могут осуществлять выборку по ID, потому что они не читают объявления DTD и схему XML. Это одна из задач, которые призван решить XSLT 2.0 — сделать доступной информацию об идентификаторах.

    last() 

    Функция

    last
    возвращает число узлов в наборе узлов, поэтому ее значение равно позиции последнего узла. Она применяется следующим образом:

    number last()

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

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

    local-name()

    Функция

    local-name
    возвращает локальное (неполное) имя узла. Функция применяется следующим образом:

    string local-name(node-set?)

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

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

    <xsl:element>
    и определяю имена контекстных узлов при помощи
    local-name
    :

    <?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:element name="{local-name(.)}">

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

        <xsl:element name="{local-name(.)}">

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

          <xsl:element name="DATA">

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

          </xsl:element>

         </xsl:for-each>

        </xsl:element>

       </xsl:for-each>

      </xsl:element>

     </xsl:template>

    </xsl:stylesheet>

    name() 

    Функция

    name
    аналогична
    local-name
    , за тем исключением, что она возвращает полностью определенное имя узла. Вот как следует применять эту функцию:

    string name(node-set?)

    namespace-uri()

    Функция

    namespace-uri
    возвращает строку, содержащую URI пространства имен в расширенном имени узла. Как правило, это URI в объявлении пространства имен, установленное при помощи атрибутов
    xmlns
    или
    xmlns:prefix
    . Функция применяется следующим образом (заметьте, что эту функцию можно применять только к элементам или атрибутам, результатом обработки остальных узлов будет пустая строка):

    string namespace-uri(node-set?)

    Например, в

    planets.xml
    я могу добавить пространство имен «star» (звезда):

    <?xml version="1.0"?>

    <?xml-stylesheet type="text/xml"

     href="planets.xsl"?>

     <star:PLANETS xmlns:star="http://starpowder.com">

      <star:PLANET>

       <star:NAME>Mercury</star:NAME>

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

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

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

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

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

      </star:PLANET>

      <star:PLANET>

       <star:NAME>Venus</star:NAME>

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

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

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

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

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

      </star:PLANET>

      .

      .

      .

    И я могу найти URI этого пространства имен в таблице стилей при помощи

    namespace-uri
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     xmlns:star="http://www.starpowder.com">

     <xsl:template match="/PLANETS">

      <xsl:value-of select="namespace-uri()"/>

     </xsl:template>

     .

     .

     .

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

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

    http://starpowder.com

    position()

    Функция

    position
    возвращает позицию контекстного узла:

    number position()

    С этой функцией мы встречались на протяжении всей книги. Такой пример вы уже видели раньше в этой главе, в нем для создания предложения «The first three planets are: Mercury, Venus, and Earth.» используются функции

    last
    и
    position
    :

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

    Функции XPath для работы со строками

    В XSLT доступны следующие функции XPath для работы со строками:

    • 

    concat(string string1, string string2,...)
    . Возвращает конкатенацию (объединение) всех переданных ей строк;

    • 

    contains(string string1, string string2)
    . Возвращает истину, если первая строка содержит (contains) вторую строку;

    • 

    normalize-space(string string1)
    . Возвращает строку
    string1
    (или контекстный узел, если строки
    string1
    нет) после отбрасывания лидирующих и завершающих символов-разделителей и замены нескольких последовательных разделителей на один пробел;

    • 

    starts-with(string string1, string string2)
    . Возвращает истину, если первая строка начинается (starts with) со второй подстроки;

    • 

    string(object)
    . Преобразует объект в строку;

    • 

    string-length(string string1)
    . Возвращает количество символов в строке
    string1
    ;

    • 

    substring(string string1, number offset, number length)
    . Возвращает
    length
    символов из строки, начиная со смещения
    offset
    ;

    • 

    substring-after(string string1, string string2)
    . Возвращает часть строки
    string1
    после первого вхождения
    string2
    ;

    • 

    substring-before(string string1, string string2)
    . Возвращает часть строки
    string1
    вплоть до первого вхождения строки
    string2
    ;

    • 

    translate(string string1, string string2, string string3)
    . Возвращает строку
    string1
    , в которой все вхождения символов в строке
    string2
    заменены на соответствующие символы в строке
    string3
    ;

    В последующих разделах я рассмотрю каждую из этих функций.

    concat()

    Функция

    concat
    объединяет вместе все переданные ей строки и возвращает полученную строку:

    concat(string string1, string string2, ...)

    В качестве примера рассмотрите разработанный ранее вариант таблицы стилей

    planets.xsl
    , отображающей значения элементов и значения атрибутов
    UNITS
    , в котором шаблоны применяются следующим образом:

    <xsl:template match="MASS">

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

     <xsl:text> </xsl:text>

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

    </xsl:template>

    Такой код отображает строковое значение, контекстный узел, пробел и строковое значение атрибута UNITS. Но код можно значительно сократить при помощи функции

    concat
    (листинг 8.8).

    Листинг 8.8. Применение функции concat

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </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="concat(., ' ', @UNITS)"/>

     </xsl:template>


     <xsl:template match="RADIUS">

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

     </xsl:template>


     <xsl:template match="DAY">

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

     </xsl:template>

    </xsl:stylesheet>

    contains()

    Функция

    contains
    проверяет, содержится ли одна строка внутри другой; если это так, функция возвращает истину, если нет — ложь. Функция применяется следующим образом:

    boolean contains(container-string, contained-string)

    Следующий пример взят из главы 7; в этом случае я хочу осуществить поиск слова «miles» во всех атрибутах, и если оно будет найдено, добавить в результирующий документ текст «You should switch to kilometers.» (Нужно перевести в километры.):

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

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

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

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

      <xsl:text> </xsl:text>

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

     </xsl:template>


     <xsl:template match="//*[contains(@UNITS, 'miles')]">

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

      <xsl:text> </xsl:text>

      <xsl:text>You should switch to kilometers.</xsl:text>

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

        <TD>Distance</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

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

        <TD>1516 You should switch to kilometers.</TD>

        <TD>58.65 days</TD>

        <TD>43.4 You should switch to kilometers.</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

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

        <TD>3716 You should switch to kilometers.</TD>

        <TD>116.75 days</TD>

        <TD>66.8 You should switch to kilometers.</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2107 You should switch to kilometers.</TD>

        <TD>1 days</TD>

        <TD>128.4 You should switch to kilometers.</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    normalize-space()

    Функция

    normalize-space
    удаляет лидирующие и завершающие символы-разделители и сжимает все внутренние идущие подряд разделители в один пробел, возвращая полученную строку. Функция применяется следующим образом:

    string normalize-space(string?)

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

    UNITS
    элемента
    <MASS>
    Меркурия:

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

     .

     .

     .

    Функция

    normalize-space
    поможет удалить лишние пробелы в таблице стилей:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </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="normalize-space(@UNITS)"/>

     </xsl:template>

     .

     .

     .

    И вот результат — обратите внимание на то, что лишние пробелы убраны:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58.65 days</TD>

       </TR>

       .

       .

       .

    starts-with()

    Как можно догадаться по имени (начинается с), функция

    starts-with
    проверяет, начинается ли одна строка с другой.

    boolean starts-with(string-to-examine, possible-start-string)

    В этом примере из главы 4 при помощи

    starts-with
    выбираются текстовые узлы, текст в которых начинается с «Е», для того чтобы выбрать Earth (Землю). Затем в описание Земли добавляется текст «(the World)» (мир), и получается «Earth (the World)»:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        .

        .

        .

       </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="text()[starts-with(., 'E')]">

      <xsl:text>(the World)</xsl:text>

     </xsl:template>


     <xsl:template match="NAME">

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

      <xsl:text> </xsl:text>

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

      <xsl:apply-templates/>

     </xsl:template>


     <xsl:template match="DAY">

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

      <xsl:text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

    Вот результат — заметьте, что подпись для Земли теперь выглядит как «Earth (the World)»:

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       .

       .

       .

       <TR>

        <TD>Earth (the World</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    string()

    Функция

    string
    просто преобразует переданный ей объект в строку. Функция применяется следующим образом:

    string string(object?)

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

    <PLANET>
    в
    planets.xml
    по три элемента
    <NAME>
    и хотите использовать в качестве фактического названия планеты только первый элемент
    <NAME>
    :

    <?xml version="1.0"?>

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

    <PLANETS>

     <PLANET>

      <NAME>Mercury</NAME>

      <NAME>Venus</NAME>

      <NAME>Earth</NAME>

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

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

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

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

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

     </PLANET>

     <PLANET>

      <NAME>Venus</NAME>

      <NAME>Earth</NAME>

      <NAME>Mercury</NAME>

      <NAME>Planet of Love.</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>

      <NAME>Mercury</NAME>

      <NAME>Venus</NAME>

      <NAME>The planet you're standing on.</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>

    Предположим теперь, что вам нужно выбрать определенную планету, например, Венеру (Venus). Такая проверка не пройдет, потому что

    NAME
    вернет набор узлов из всех дочерних элементов
    <NAME>
    контекстного узла, а так как у каждой планеты есть элемент
    <NAME>
    со значением «Venus», такое условие всегда будет истиной:

    <xsl:template match="PLANET">

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

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

    </xsl:template>

    Для того чтобы проверить первый элемент

    <NAME>
    в каждом элементе
    <PLANET>
    , можно воспользоваться функцией
    string
    , поскольку она возвращает не набор узлов, а строку:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

      <xsl:if test="string(NAME)='Venus'">

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

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

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

    <NAME>
    контекстного узла, проще воспользоваться
    NAME[1]
    .

    string-length()

    Как можно предположить, функция

    string-length
    возвращает длину (length) переданной ей строки. Функция применяется таким образом:

    number string-length(string?)

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

    string-length
    :

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

         Length of Planet Names

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         Length of Planet Names

        </H1>

        <xsl:apply-templates/>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

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

      <xsl:value-of select-"string-length(NAME)"/>

      characters long.

      <BR/>

     </xsl:template>


     <xsl:template match="*">

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       Length of Planet Names

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       Length of Planet Names

      </H1>

      Mercury is 7 characters long.

      <BR>

      Venus is 5 characters long.

      <BR>

      Earth is 5 characters long.

      <BR>

     </BODY>

    </HTML>

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

    Рис. 8.2. Определение длины строк в XSLT

    substring()

    Функция

    substring
    возвращает подстроку переданной ей строки. Функция применяется следующим образом:

    string substring(source-string, start-position, number-of-characters?)

    Функция принимает параметры

    source-string
    (исходная строка),
    start-position
    (начальная позиция) и необязательный параметр
    number-of-characters
    (количество символов). Функция возвращает подстроку исходной строки с начальной позиции и до указанного количества символов или до конца строки, если это количество не задано.

    Функция

    substring
    — одна из трех функций, оперирующих с подстроками:
    substring-before
    , возвращающая строку перед найденной подстрокой, сама
    substring
    , возвращающая заданную подстроку, и
    substring-after
    , возвращающая строку после найденной подстроки. В следующем примере (листинг 8.9) задействованы все три функции: в этом случае я разбил название планеты Меркурий (Mercury) на три подстроки — «Mer», «c» и «ury» — и затем объединил их снова. Вот как это можно сделать при помощи трех рассматриваемых функций (дополнительная информация о
    substring-before
    и
    substring-after
    приведена в следующих двух разделах).

    Листинг 8.9. Применение функций substring-before, substring и substring-after

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

         Planetary Information

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         Planetary Information

        </H1>

        <xsl:apply-templates/>

       </BODY>

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET">

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

       The first planet is

       <xsl:value-of select="concat(substring-before(NAME, 'с'),

        substring(NAME, 4, 1), substring-after(NAME, 'c'))"/>.

       <BR/>

      </xsl:if>

     </xsl:template>


     <xsl:template match="*">

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       Planetary Information

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       Planetary Information

      </H1>

      The first planet is Mercury.

      <BR>

     </BODY>

    </HTML>

    substring-after()

    Функция

    substring-after
    возвращает подстроку, которая следует за найденной строкой. В эту функцию передается строка, подстрока, которую нужно найти внутри строки, — и функция возвращает подстроку, расположенную после совпадающей части строки, если она была найдена; иначе функция возвращает пустую строку. Функция применяется следующим образом:

    string substring-after(string, string-to-match)

    substring-before()

    В функцию

    substring-before
    передается строка, подстрока, которую нужно найти внутри строки, и функция возвращает подстроку, расположенную перед совпадающей частью строки, если она была найдена; иначе функция возвращает пустую строку. Функция применяется следующим образом:

    string substring-before(string, string-to-match)

    В разделе

    substring()
    приведен пример применения функций
    substring-before
    ,
    substring
    и
    substring-after
    .

    translate()

    Функция

    translate
    служит для перевода или замены определенных символов. (Эта функция во многом похожа на операцию tr в Perl, если вы знакомы с операциями.) В функцию передаются две строки: одна задает список символов поиска, вторая задает список символов, которыми нужно заменить найденные символы. Функция применяется следующим образом:

    string translate(string, from-characters, to-characters)

    Например, если третий символ из

    from-characters
    будет найден в
    string
    , то третий символ из
    to-characters
    заменит его в результирующей строке. В следующем примере такая функция
    translate

    translate("steve-starpowder.com", "-", "@")

    возвратит результирующую строку «steve@starpowder.com».

    В этом примере я просто привожу строку к нижнему регистру:

    translate("XSLT", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")

    Функции XPath для работы с числами

    В XPath имеются следующие функции для работы с числами:

    • 

    ceiling()
    . Возвращает наименьшее целое, большее переданного функции числа,

    • 

    floor()
    . Возвращает наибольшее целое, меньшее переданного функции числа;

    • 

    number()
    . Приводит переданный ей параметр к числу;

    • 

    round()
    . Округляет переданное функции число до ближайшего целого;

    • 

    sum()
    . Возвращает сумму переданных функции чисел.

    В следующих разделах мы рассмотрим примеры применения каждой из этих функций.

    ceiling()

    Функция

    ceiling
    возвращает наименьшее целое, которое все еще больше переданного функции числа, то есть она возвращает следующее большее целое. Функция применяется следующим образом:

    number ceiling(number)

    Например, выражение

    ceiling(3.1415926535)

    возвращает 4.

    floor()

    Функция

    floor
    — парная к функции
    ceiling
    . Она возвращает наибольшее целое, которое все еще меньше переданного ей числа. Другими словами, функция возвращает предыдущее целое. Функция применяется следующим образом:

    number floor(number)

    В этом примере я при помощи

    floor
    преобразую длительность дней планет в целые числа:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </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="floor(.)"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       The Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

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

        <TD>1516 miles</TD>

        <TD>58 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

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

        <TD>3716 miles</TD>

        <TD>116 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    number()

    Функция

    number
    приводит свой аргумент к числу. Функция применяется следующим образом:

    number number(object?)

    В этом примере я передаю

    number
    строку:

    number("456.7")

    Это выражение возвращает число 456.7. Если вы попытаетесь преобразовать что-то, что

    number
    не сможет перевести в число, функция вернет значение XPath
    NaN
    (Not a Number, не число).
    NaN
    — корректное значение, которое можно проверять в выражениях Xpath.

    round()

    Функция

    round
    округляет переданное ей значение. Функция применяется следующим образом:

    number round(number)

    Например,

    round(3.1415926535)
    возвращает 3,
    round(4.5)
    возвращает 5 и
    round(-1.5)
    возвращает -1.

    sum()

    Функция

    sum
    суммирует численные значения набора узлов и возвращает результат. Функция применяется следующим образом:

    number sum(node-set)

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

    planets.xml
    :

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

      <HTML>

       <BODY>

        The average planetary mass is

        <xsl:value-of select="sum(child::PLANET/child::MASS) div count(child::PLANET/child::MASS)"/>

       </BODY>

      </HTML>

     </xsl:template>

    </xsl:stylesheet>

    Логические функции XPath

    XPath также поддерживает следующий набор логических функций:

    • 

    boolean()
    . Приводит аргумент к логическому значению;

    • 

    false()
    . Возвращает false (ложь);

    • 

    lang()
    . Проверяет, совпадает ли язык, установленный в атрибуте
    xml:lang
    , с языком, переданным в функцию;

    • 

    not()
    . Инвертирует значение true\false аргумента;

    • 

    true()
    . Возвращает true (истина).

    boolean()

    Функция

    boolean
    преобразует аргумент в логическое значение. Функция применяется следующим образом:

    boolean boolean(object)

    Ниже показано, как функция работает с различными типами XPath:

    • 

    number
    . Если число равно нулю, результатом будет ложь; иначе результат — истина. NaN всегда возвращает ложь;

    • 

    string
    . Если строка не пуста, результат — истина, иначе — ложь;

    • 

    boolean
    . Значение не изменяется;

    • 

    nodeset
    . Пустой набор узлов дает ложь, не пустой — истину;

    • Фрагмент результирующего дерева в XSLT 1.0 всегда дает истину.

    false()

    Функция

    false
    возвращает ложь. Она применяется следующим образом:

    boolean false()

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

    false
    . (С переменными вы познакомитесь в главе 9.)

    lang() 

    Функция

    lang
    проверяет, совпадает ли язык контекстного узла (определенный в атрибуте
    xml:lang
    ) с переданным ей языком. Функция применяется следующим образом:

    boolean lang(string)

    Функции нужно передать строку, определяющую язык в спецификации XML, как, например, «en» для английского, «de» для немецкого и «jp» для японского.

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

    xml:lang
    в
    planets.xml
    в значение, соответствующее английскому языку:

    <?xml version="1.0"?>

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

    <PLANETS xml:lang="en">

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

     .

     .

     .

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

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <xsl:if test="lang('en')">

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

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

    not()

    Функция

    not
    инвертирует логическое значение аргумента. При передаче аргумента с логическим значением true она возвращает false; при передаче аргумента со значением false функция возвращает true. Функция применяется следующим образом:

    boolean not(boolean)

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

    COLOR
    и
    POPULATED
    . Для этого я применяю предикат выбора «
    [@COLOR and (@POPULATED]
    ». Для того, чтобы убрать другие элементы (чтобы правила по умолчанию не включали их текст в результирующий документ), я использую предикат «
    [not(@COLOR) or not(@POPULATED)]
    »:

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:template match="/PLANETS">

      <HTML>

       .

       .

       .

      </HTML>

     </xsl:template>


     <xsl:template match="PLANET[@COLOR and @POPULATED]">

      <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="PLANET[not(@COLOR) or not(@POPULATED)]">

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

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

    <HTML>

     <HEAD>

      <TITLE>

       Colorful, Populated Planets

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       Colorful, Populated Planets

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

    true()

    Функция

    true
    возвращает значение true. Она применяется следующим образом:

    boolean true()

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

    true
    . (С переменными вы познакомитесь в главе 9.)

    Элемент <xsl:decimal-format>: создание числовых форматов

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

    <xsl:decimal-format>
    , единственное предназначение которого заключается в работе только с одной функцией
    format-number
    . В частности, при помощи этого элемента определяются символы и идентификаторы, которые будет использовать
    format-number
    . У этого элемента несколько атрибутов:

    • 

    name
    (необязательный). Имя десятичного формата. Принимает значение типа
    QName
    . Если формат не задан, используется десятичный формат по умолчанию;

    • 

    decimal-separator
    (необязательный). Определяет символ, разделяющий целую и дробную части числа. По умолчанию «.». Устанавливается в символ;

    • 

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

    • 

    infinity
    (необязательный). Задает строку, обозначающую положительную бесконечность. По умолчанию «Infinity» (бесконечность). Устанавливается в строку;

    • 

    minus-sign
    (необязательный). Определяет символ, представляющий знак минуса. По умолчанию «-». Устанавливается в символ;

    • 

    NaN
    (необязательный). Определяет строку, представляющую значение «Not a Number» (не число). По умолчанию «NaN». Устанавливается в строку;

    • 

    percent
    (необязательный). Определяет символ, представляющий знак процента. По умолчанию «%». Устанавливается в символ;

    • 

    per-mille
    (необязательный). Определяет символ, представляющий знак промилле, то есть тысячных долей. По умолчанию «‰». Устанавливается в символ;

    • 

    zero-digit
    (необязательный). Определяет символ для использования в форматирующих строках для указания лидирующего или завершающего нуля. По умолчанию «0». Устанавливается в символ;

    • 

    digit
    (необязательный). Определяет символ для использования в форматирующих строках для указания цифры. По умолчанию «0». Устанавливается в символ;

    • 

    pattern-separator
    (необязательный). Определяет символ, разделяющий образцы для положительных и отрицательных чисел. По умолчанию «;». Устанавливается в символ.

    Этот элемент — верхнего уровня, и он всегда пуст. При помощи этого элемента можно устанавливать форматирующие символы для функции

    format-number
    . Элемент
    xsl:decimal-format
    , у которого не установлен
    атрибут
    name, становится десятичным форматом по умолчанию. Наличие более одного элемента
    xsl:decimal-format
    по умолчанию или нескольких элементов
    xsl:decimal-format
    с одним и тем же именем является ошибкой. В следующем примере я форматирую числа в
    planets.xml
    при помощи европейского формата числа, а именно — я использую запятую вместо десятичной точки для разделения целой и дробной частей, и точку вместо запятой для разделения групп разрядов. Для этого мне нужно только задать новое форматирование при помощи
    <xsl:decimal-format>
    и затем применить его в
    format-number
    :

    <?xml version="1.0"?>

    <xsl:stylesheet version="1.0"

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

     <xsl:decimal-format decimal-separator="," grouping-separator="."/>

     <xsl:template match="/PLANETS">

      <HTML>

       <HEAD>

        <TITLE>

         The Formatted Planets Table

        </TITLE>

       </HEAD>

       <BODY>

        <H1>

         The Formatted 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">

      <xsl:value-of select="format-number(., '#.##')"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>


     <xsl:template match="RADIUS">

      <xsl:value-of select="format-number(., '#.##')"/>

      <xsl:text> </xsl:text>

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

     </xsl:template>


     <xsl:template match="DAY">

      <xsl:value-of select="format-number(., '###.#')"/>

      <xsl text> </xsl:text>

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

     </xsl:template>

    </xsl:stylesheet>

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

    <HTML>

     <HEAD>

      <TITLE>

       The Formatted Planets Table

      </TITLE>

     </HEAD>

     <BODY>

      <H1>

       The Formatted Planets Table

      </H1>

      <TABLE BORDER="2">

       <TR>

        <TD>Name</TD>

        <TD>Mass</TD>

        <TD>Radius</TD>

        <TD>Day</TD>

       </TR>

       <TR>

        <TD>Mercury</TD>

        <TD>0,055 (Earth = 1)</TD>

        <TD>1,516 miles</TD>

        <TD>58,65 days</TD>

       </TR>

       <TR>

        <TD>Venus</TD>

        <TD>0,815 (Earth = 1)</TD>

        <TD>3,716 miles</TD>

        <TD>116,75 days</TD>

       </TR>

       <TR>

        <TD>Earth</TD>

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

        <TD>2,107 miles</TD>

        <TD>1 days</TD>

       </TR>

      </TABLE>

     </BODY>

    </HTML>

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

    Рис. 8.3. Задание десятичных форматов


    Вот и все — теперь вы можете задавать параметры форматирования для функции

    format-number
    .

    Новые функции в XSLT 2.0 и XPath 2.0

    В XSLT 2.0 и XPath 2.0 планируется включить весьма много функций. В частности, для XSLT 2.0 внесены в план следующие положения:

    • должны быть включены функции, которые могут работать с

    QName
    , для того, чтобы можно было применить объявления пространств имен из области видимости;

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

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

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

    В XPath 2.0 в отношении функций планируются следующие изменения:

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

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

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

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

    • должны быть определены функции для работы со стандартными типами схемы XML.

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







     


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