XSLT

Холзнер Стивен

Глава 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) узел — но не контекстный узел. Текущий узел — это узел, используемый в данный момент в таких циклах, как . Функцию 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) — узел. Контекстный узел шаблона — это узел в выбранном наборе узлов, к которому применяется шаблон. Текущий узел, с другой стороны, это используемый в данный момент узел в таких циклах, как . Функция возвращает текущий узел как набор узлов с одним узлом:

node-set current()

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

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

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

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

 

  

   .

   .

   .

   

    

     

     

     

     

    

    

   

Name Mass Radius Day

  

 

 

 

 

   

    

    

   

    

   

 

 

 

  

  

  

 

 

  

  

  

 

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

 

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

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

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

 Mercury

 .0553

 58.65

 1516

 .983

 43.4

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

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

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

 

  

   

     </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

   

    

     

     

     

     

     

    

    

   

Name Mass Radius Day

   

  

 

 

  

  

   

   

   

  

 

 .

 .

 .

 

  

  

  

 

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

  

   

   

    

    

    

   

  

Name Mass Radius Day
Mercury .0553 (Earth = 1) 1516 miles 58.65 days

 

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

 

element-available()

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

boolean element-available(element-name)

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

Мы уже встречались с этой функцией в главе 5. В примере element-available той главы я проверял наличие элемента таким образом:

 

 

 

 

  Sorry, can't do math today.

 

 

format-number()

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

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

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

Форматирующая строка 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 (эти символы можно изменить при помощи элемента , который будет рассмотрен в конце этой главы):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 

  .

  .

  .

 

 

  

  

   

   

   

  

 

 

 

  

  

 

 

 

  

  

 

 

 

  

  

 

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

 

  </p> <p class="paragraph">    The Formatted Planets Table </p> <p class="paragraph">   

 

 

  

   The Formatted Planets Table

  

 

   

   

    

    

    

   

  

   

    

    

    

   

  

   

    

    

    

   

  

   

    

    

    

   

  

Name Mass Radius Day
Mercury 0.055 (Earth = 1) 1.516 miles 58.65 days
Venus 0.815 (Earth = 1) 3.716 miles 116.75 days
Earth 1 (Earth = 1) 2.107 miles 1 days

 

Этот результирующий документ показан на рис. 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.» (Извините, сегодня математические вычисления не работают.). Можно, конечно, также прекратить обработку и вывести сообщение об ошибке при помощи элемента :

 

 

 

 

  Sorry, can't do math today.

 

 

generate-id()

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

string generate-id(node)

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

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

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

 

  

   

    </p> <p class="paragraph">      The Planets Table </p> <p class="paragraph">     

   

   

    

     The Planets Table

   

   

     

     

    

     

    

    .

    .

    .

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

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

 

  

   .

   .

   .

 

 

 

  

  

     

  

  

  

   

  

 

 

  

  

  

 

 .

 .

 .

 

  

  

  

 

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

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

  

   Mercury

  

 

 

   Venus

  

 

  

   Earth

 

 

 

   

   

    

    

    

   

  

   

    

   

    

   

  

   

    

    

    

   

   

    

    

    

    

   

 

Name Mass Radius Day
Mercury .0553 (Earth = 1) 1516 miles 58.65 days
Venus .815 (Earth = 1) 3716 miles 116.75 days
Earth 1 (Earth = 1) 2107 miles 1 days

 

 

key()

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

node-set key(name, value)

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

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

 .

 .

 .

 

  Earth

  "(Earth = 1)">1

  1

  2107

  1

  128.4

 

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

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

 

 .

 .

 .

Теперь я могу применить образец «key()» для выбора элементов с атрибутом COLOR со значением «BLUE» таким образом:

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

 

 

 

   .

   .

   .

   

     

     

      

      

     

    

     

    

Name Mass Radius Day

   

  

 

 .

 .

 .

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

 <НЕАD>

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

   

  

   

    

    

    

   

  

Name Mass Radius Day
Earth 1 (Earth = 1) 2107 miles 1 days

 

 

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');

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

 .

 .

 .

 

 

 

 .

 .

 .

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

 

unparsed-entity-uri()

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

string unparsed-entity-uri(name)

В функцию передается имя name неразобранной сущности, и функция возвращает для нее идентификатор. В следующем примере (листинг 8.6) я добавил в planets.xml DTD и объявил три неразобранных сущности, соответствующих рисункам планет — image1, image2 и image3, — и обратился к ним, добавив в каждый элемент атрибут IMAGE.

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

]>

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 

  Venus

  .815

  116.75

  3716

  .943

  66.8

 

 

  Earth

  1

  1

  2107

  1

  128.4

 

Функции 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. В этом случае набор узлов образован из всех элементов в planets.xml; я получил его при помощи пути расположения «//PLANET»:

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

 

 

  

  

  

 

 

 

  

  

 

 

 

 

  

 

 

Заметьте, что в приведенном ниже результате каждый элемент обладает и атрибутом number, и атрибутом total, а атрибут total хранит общее количество элементов , которое было найдено при помощи count:

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 

  Venus

  .815

  116.75

  3716

  .943

  66.8

 

 

  Earth

  1

  1

  2107

  1

  128.4

 

 

id()

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

node-set id(id-value)

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

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

 

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

 id ID #REQUIRED>

]>

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 .

 .

 .

Заметьте, что некоторые процессоры 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 и проверяя позицию при помощи :

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

 

 

  

    </p> <p class="paragraph">     The Planets </p> <p class="paragraph">    

   

    The first three planets are:

    

   

  

 

 

  

  ,

  and

  .

 

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

  </p> <p class="paragraph">   The Planets </p> <p class="paragraph">  

 

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

 

 

local-name()

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

string local-name(node-set?)

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

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

 

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

 

 

 

   

   

     

      

      

      

     

    

   

  

 

 

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» (звезда):

 href="planets.xsl"?>

 

 

   Mercury

   .0553

   58.65

   1516

   .983

   43.4

  

 

   Venus

   .815

   116.75

   3716

   .943

   66.8

  

  .

  .

  .

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

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

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

 

 

 

 .

 .

 .

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

http://starpowder.com

 

position()

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

number position()

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

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

 

 

  

    </p> <p class="paragraph">     The Planets </p> <p class="paragraph">    

   

    The first three planets are:

    

   

  

 

 

  

  ,

  and

  .

 

Полученный результат:

  </p> <p class="paragraph">   The Planets </p> <p class="paragraph">  

 

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

 

 

Функции 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, в котором шаблоны применяются следующим образом:

 

 

 

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

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

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

 

  

   .

   .

   .

 

 

 

  

  

   

   

   

  

 

 

  

 

 

 

 

 

 

 

 

contains()

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

boolean contains(container-string, contained-string)

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

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

 

  

   .

   .

   .

 

 

 

  

  

   

   

   

   

  

 

 

  

  

  

 

 .

 .

 .

 

  

  

  

 

 

  

  

  You should switch to kilometers.

 

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

    

   

  

   

    

   

   

   

  

  

   

   

   

   

   

   

  

   

    

   

   

   

  

 

Name Mass Radius Day Distance
Mercury .0553 (Earth = 1) 1516 You should switch to kilometers. 58.65 days 43.4 You should switch to kilometers.
Venus .815 (Earth = 1) 3716 You should switch to kilometers. 116.75 days 66.8 You should switch to kilometers.
Earth 1 (Earth = 1) 2107 You should switch to kilometers. 1 days 128.4 You should switch to kilometers.

 

 

normalize-space()

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

string normalize-space(string?)

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

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 .

 .

 .

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

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

 

  

   .

   .

   .

 

 

 

  

  

   

   

   

  

 

 

  

  

 

 

 .

 .

 .

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

   

  

   

    

   

    

   

   .

   .

   .

 

starts-with()

Как можно догадаться по имени (начинается с), функция starts-with проверяет, начинается ли одна строка с другой.

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

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

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

 

  

   

    .

    .

    .

  

 

 

 

  

  

   

  

   

  

 

 

  (the World)

 

 

  

  

  

  

 

 

  

  

  

 

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

Name Mass Radius Day
Mercury .0553 (Earth = 1) 1516 miles 58.65 days

   

   

    

    

    

  

   .

   .

   .

  

   

    

    

    

   

  

Name Mass Radius Day
Earth (the World 1 (Earth = 1) 2107 miles 1 days

 

 

string()

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

string string(object?)

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

 

  Mercury

  Venus

  Earth

  .0553

  58.65

  1516

  .983

  43.4

 

 

  Venus

  Earth

  Mercury

  Planet of Love.

  .815

  116.75

  3716

  .943

  66.8

 

 

  Earth

  Mercury

  Venus

  The planet you're standing on.

  1

  1

  2107

  1

  128.4

 

Предположим теперь, что вам нужно выбрать определенную планету, например, Венеру (Venus). Такая проверка не пройдет, потому что NAME вернет набор узлов из всех дочерних элементов контекстного узла, а так как у каждой планеты есть элемент со значением «Venus», такое условие всегда будет истиной:

 

 

  

   

   

   

  

 

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

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

 

  

   .

   .

   .

 

 

 

  

  

   

    

   

    

   

  

 

 

  

  

  

 

 .

 .

 .

 

  

  

  

 

Конечно, если нужно только выбрать первый дочерний элемент контекстного узла, проще воспользоваться NAME[1]См. выпущенные издательством книги «XML и Java 2, библиотека программиста» и «XML, справочник». — Примеч. ред.
.

 

string-length()

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

number string-length(string?)

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

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

 

  

   

     </p> <p class="paragraph">      Length of Planet Names </p> <p class="paragraph">     

   

   

    

     Length of Planet Names

    

   

  

  

 

 

  is

  

  characters long.

 

 

 

 

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

 

   </p> <p class="paragraph">    Length of Planet Names </p> <p class="paragraph">   

 

 

  

   Length of Planet Names

  

  Mercury is 7 characters long.

  

  Venus is 5 characters long.

  

  Earth is 5 characters long.

  

 

Этот результирующий документ показан на рис. 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

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

 

  

   

     </p> <p class="paragraph">      Planetary Information </p> <p class="paragraph">     

   

   

    

     Planetary Information

    

   

  

  

 

 

  

   The first planet is

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

   

  

 

 

 

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

 

   </p> <p class="paragraph">    Planetary Information </p> <p class="paragraph">   

 

 

  

   Planetary Information

  

  The first planet is Mercury.

  

 

 

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", "-", "@")

возвратит результирующую строку «[email protected]».

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

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 преобразую длительность дней планет в целые числа:

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

 

  

   .

   .

   .

 

 

 

  

  

   

   

   

  

 

 

  

 

  

 

 

  

  

  

 

 

 

  

  

 

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

 

   </p> <p class="paragraph">    The Planets Table </p> <p class="paragraph">   

 

 

  

   The Planets Table

  

 

   

   

    

    

    

   

  

   

    

   

    

   

  

   

    

    

    

   

   

    

   

   

    

  

 

Name Mass Radius Day
Mercury .0553 (Earth = 1) 1516 miles 58 days
Venus .815 (Earth = 1) 3716 miles 116 days
Earth 1 (Earth = 1) 2107 miles 1 days

 

 

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:

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

 

 

  

   

    The average planetary mass is

   

  

  

 

 

Логические функции 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 в значение, соответствующее английскому языку:

 

  Mercury

  .0553

  58.65

  1516

  .983

  43.4

 

 .

 .

 .

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

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

 

 

   

    

      </p> <p class="paragraph">       The Planets Table </p> <p class="paragraph">      

    

    

     

      The Planets Table

     

    

      

      

       

       

       

      

     

     

Name Mass Radius Day

   

  

  

 

 

 

  

   

   

   

  

 

 

not()

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

boolean not(boolean)

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

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

 

  

   .

   .

   .

 

 

 

  

  

   

   

   

  

 

 

 

 

  

  

  

 

 

  

  

  

 

 

  

  

  

 

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

 

   </p> <p class="paragraph">    Colorful, Populated Planets </p> <p class="paragraph">   

 

 

  

   Colorful, Populated Planets

  

 

   

   

    

    

    

  

  

   

    

    

    

   

  

Name Mass Radius Day
Earth 1 (Earth = 1) 2107 miles 1 days

 

 

true()

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

boolean true()

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

 

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

Перед тем как закончить обсуждение функций XSLT и XPath, я хочу рассмотреть специальный элемент XSLT, , единственное предназначение которого заключается в работе только с одной функцией 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 при помощи европейского формата числа, а именно — я использую запятую вместо десятичной точки для разделения целой и дробной частей, и точку вместо запятой для разделения групп разрядов. Для этого мне нужно только задать новое форматирование при помощи и затем применить его в format-number:

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

 

 

  

   

     </p> <p class="paragraph">      The Formatted Planets Table </p> <p class="paragraph">     

   

   

    

     The Formatted Planets Table

    

   

     

     

      

      

      

     

    

    

Name Mass Radius Day

   

  

 

 

  

  

  

  

  

  

 

 

 

 

  

 

 

 

 

 

 

 

 

  

  

 

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

 

  </p> <p class="paragraph">    The Formatted Planets Table </p> <p class="paragraph">   

 

 

  

   The Formatted Planets Table

  

 

   

   

    

    

    

   

  

   

    

    

    

   

  

   

    

    

    

   

  

   

    

    

    

   

 

Name Mass Radius Day
Mercury 0,055 (Earth = 1) 1,516 miles 58,65 days
Venus 0,815 (Earth = 1) 3,716 miles 116,75 days
Earth 1 (Earth = 1) 2,107 miles 1 days

 

Этот документ показан на рис. 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 мы изучим переменные, параметры и другие специальные темы.