Технология XSLT — страница 25 из 66

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

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

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

FunctionCall
:

[XP16] FunctionCall ::= FunctionName

                        '(' ( Argument ( ',' Argument )* )? ')'

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

тип1 функция(тип2, тип3, тип4?)

где

тип1
— тип возвращаемого значения,
тип2
,
тип3
,
тип4
— типы передаваемых параметров, символ "
?
" обозначает аргумент, который может быть опущен. Также может быть использован символ
*
для обозначения аргумента, который может повторяться несколько раз. Например,

string concat(string, string, string*)

определяет функцию

concat
, которая возвращает строку, а на вход принимает два или более строковых параметра.

Аргументы функции отвечают EBNF-продукции

Argument
:

[XP17] Argument ::= Expr

Имя функции определяется синтаксическим правилом

FunctionName
. Функция может иметь любое корректное с точки зрения XML имя, кроме названий типов узлов (
comment
,
processing-instruction
,
text
и
node
):

[XP35] FunctionName ::= QName - NodeType

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

Булевые функцииФункция boolean

boolean boolean(object)

Функция

boolean
явным образом преобразует объект, который ей передается в булевый тип в соответствии с правилами, изложенными в главе "Типы данных XPath". Напомним вкратце эти правила.

□ Число преобразуется в "ложь", если оно является положительным или отрицательным нулем или не-числом (

NaN
). В противном случае число будет преобразовано в "истину".

□ Строка преобразуется в "ложь", если она не содержит символов, то есть, ее длина равна нулю. Непустая строка преобразуется в "истину".

□ Множество узлов преобразуется в "ложь", если оно пусто. Непустое множество узлов преобразуется в "истину".

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

Примеры:

boolean(2-2) 
 false

boolean(number('two')) 
 false

boolean(-1) 
 true

boolean(1 div 0) 
 true

boolean(-1 div (1 div 0)) 
 false

boolean(-1 div (-1 div 0)) 
 false

boolean(-1 div (-1 div 0) +1) 
 true

boolean('') 
 false

boolean('true') 
 true

boolean('false') 
 true

boolean(/) 
 true

Это выражение всегда будет обращаться в

true
, поскольку у документа всегда есть корневой узел.

boolean(/self::node()) 
 true

Это выражение также обратится в

true
, поскольку корневой узел соответствует тесту
node()
.

boolean(/self::text()) 
 false

Это выражение обратится в

false
, поскольку корневой узел не является текстовым узлом.

Функция not

boolean not(boolean)

Функция

not
выполняет логическое отрицание. Если аргументом была "истина",
not
возвращает "ложь", если аргумент был "ложью",
not
вернет "истину". Если функции был передан аргумент не булевого типа (например, число), то он сначала будет сконвертирован в тип
boolean
.

Примеры:

not(false) 
 true

not(true) 
 false

not('false') 
 false

not('true') 
 false

not(0) 
 true

not(/) 
 false

Функции true и false

boolean true()

boolean false()

Две функции

true
и
false
возвращают тождественную "истину" и тождественную "ложь" соответственно. В XPath нет констант и, тем более, логических констант, определяющих "истину" и "ложь", как в других языках. Функции
true
и
false
восполняют эту нехватку.

Примеры:

true() or $var 
 true

Это выражение всегда будет истинным вне зависимости от значения переменной

var
, поскольку дизъюнкция (логическая операция "или") с тождественной "истиной" всегда будет "истиной".

false() and $var 
 false

Это выражение всегда будет ложным вне зависимости от значения переменной

var
, поскольку конъюнкция (логическая операция "и") с тождественной "ложью" всегда будет "ложью".

Функция lang

boolean lang(string)

Функция

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

Yet no living human being have been ever blessed with seeing...

Пространство имен, соответствующее префиксу

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

Функция

lang
возвратит "истину", если идентификатор языка, который передан ей в виде строкового параметра, соответствует языковому контексту контекстного узла. Это определяется следующим образом.

□ Если ни один из предков контекстного узла не имеет атрибута

xml:lang
, функция возвращает "ложь".

□ Иначе строковый параметр проверяется на соответствие значению атрибута

xml:lang
ближайшего предка. Если эти значения равны в любом регистре символов, или атрибут начинается как значение параметра функции и имеет суффикс, начинающийся знаком "
-
", функция возвращает "истину".

□ В противном случае функция возвращает "ложь".

Примеры:

Функция

lang('en')
возвратит "истину" в контексте любого из следующих элементов:

Функция

lang('de')
возвратит "истину" в контексте элемента
b
и "ложь" — в контексте элементов
а
и
с
:

<а>

Числовые функции

Функция number

number number(object?)

Функция

number
явным образом конвертирует свой аргумент в числовой тип. Если аргумент не указан, функции передается множество узлов, состоящее из единственного контекстного узла. Коротко напомним правила преобразования в числовой тип.

□ Значения булевого типа преобразуются в

0
или
1
следующим образом: "ложь" преобразуется в
0
, "истина" в
1
.

□ Строковое значение преобразуется в число, которое оно представляет.

□ Множество узлов сначала преобразуется в строку, а затем, как строка в число. Фактически численным значением множества узлов является численное значение его первого узла.

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

Примеры

number($to_be or not($to_be)) 
 1

Значение этого выражения будет

1
, поскольку
$to_be or not($to_be)
будет истинным вне зависимости от значения переменной
to_be
.

number(false()) 
 0

number('00015.0001000') 
 15.0001

number('.0001000') 
 0.0001

number('1.') 
 1

number('-.1') 
 -0.1

number('-5') 
 -5

Функция sum

number sum(node-set)

Функция

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

Пример
Листинг 6.3. Входящий документ

1

3

5

Листинг 6.4. Преобразование

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

 version="1.0">




Листинг 6.5. Результат

1

4

9

16

25

В этом преобразовании мы заменяем каждый элемент

item
на сумму значений предшествующих ему элементов плюс собственное значение. Предшествующие элементы выбираются путем выборки
preceding-sibling::item
, текущий элемент — сокращенным путем "
.
", затем эти два множества объединяются при помощи оператора
|
, и, наконец, мы вычисляем сумму значений узлов, входящих в них функцией
sum
.

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

number
. Например, если входящий документ будет иметь вид

1

3

five

7

9

то на выходе мы получим

1

4

NaN

NaN

NaN

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

number('five')
, то есть не-число (
NaN
).

Функции floor и ceiling

number floor(number)

number ceiling(number)

Функции

floor
и
ceiling
(англ. пол и потолок соответственно) осуществляют округление численного аргумента до ближайшего не большего и ближайшего не меньшего целого соответственно.

Примеры

floor(2.3) 
 2

ceiling(2.3) 
 3

floor(-2.3) 
 -3

ceiling(-2.3) 
 -2

floor(-1 div 0) 
 -Infinity

ceiling(-1 div 0) 
 -Infinity

floor('zero') 
 NaN

ceiling(-1 div (-1 div 0)) 
 0

Функция round

number round(number)

Функция

round
округляет число до ближайшего целого значения. У этой функции есть несколько нюансов, которые мы сейчас разберем.

□ Если дробная часть числа равна 0.5, то функция вернет ближайшее большее целое.

□ Если аргумент является не-числом (NaN), то результат также будет NaN.

□ Если аргумент является положительной или отрицательной бесконечностью, то результатом будет тоже положительная или отрицательная бесконечность, то есть аргумент не изменится.

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

□ Если аргумент меньше нуля, но больше или равен — 0.5, результатом будет отрицательный нуль.

Примеры

round(2.5) 
 3

round(2.49) 
 2

round(-1.7) 
 -2

1 div round(0.5) 
 Infinity

1 div round(-0.5) 
 -Infinity

round(1 div 0) 
 Infinity

round('one') 
 NaN

Строковые функции

Функция string

string string(object?)

Подобно функциям

boolean
и
number
, функция
string
преобразует свой аргумент к строковому типу явным образом. Если аргумент опущен, функции передается множество узлов, состоящее из единственного контекстного узла.

Напомним вкратце правила приведения других типов к строке.

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

 • "истина" (

true
) преобразуется в строку "
true
";

 • "ложь" (

false
) преобразуется в строку "
false
".

□ Числа преобразуются к строковому виду следующим образом:

 • не-число (

NaN
) преобразуется в строку "
NaN
";

 • положительный нуль преобразуется в строку "

0
";

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

0
";

 • положительная бесконечность преобразуется в строку "

Infinity
";

 • отрицательная бесконечность преобразуется в строку "

-Infinity
";

 • положительные целые преобразуются в свое десятичное представление без ведущих нулей и без точки ("

.
"), отделяющей дробную часть от целой;

 • отрицательные целые преобразуются так же, как и положительные, но с начальным знаком "минус" ("

-
");

 • остальные числа преобразуются в десятичное представление с использованием точки ("

.
"), отделяющей целую часть от дробной части; целая часть не содержит ведущих нулей (кроме случая с числами в интервале (-1;1)), дробная часть содержит столько цифр, сколько требуется для точного представления числа.

□ Множество узлов преобразуется в строковое значение своего первого в порядке просмотра документа узла. Если множество пусто, функция возвращает пустую строку.

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

Примеры

string(boolean(0)) 
 false

string(number('zero')) 
 NaN

string(number('.50000')) 
0.5

string(number(00500.)) 
 500

Для строкового форматирования чисел рекомендуется использовать функцию XSLT

format-number
совместно с элементом
xsl:decimal-format
.

Функция concat

string concat(string, string, string*)

Функция

concat
принимает на вход две или более строки и возвращает конкатенацию (строковое сложение) своих аргументов.

Пример:

concat('not','with','standing',' problem') 
 'notwithstanding problem'

Функция starts-with

boolean starts-with(string, string)

Функция

starts-with
принимает на вход два строковых аргумента и возвращает
true
, если первая строка начинается второй и
false
в противном случае.

starts-with('http://www.xsltdev.ru', 'http') 
 true

starts-with('Title', 'ti') 
 false

Функция contains

boolean contains(string, string)

Функция

contains
принимает на вход два строковых аргумента и возвращает
true
, если первая строка содержит вторую и
false
в противном случае.

contains('address@host.com', '(@') 
 true

Функция substring-before

string substring-before(string, string)

Функция

substring-before
принимает на вход два строковых аргумента. Эта функция находит в первой строке вторую и возвращает подстроку, которая ей предшествует. Если вторая строка не содержится в первой, функция вернет пустую строку.

Примеры

substring-before('12-May-1998', '-') 
 '12'

substring-before('12 May 1998', ' ') 
 '12'

substring-before('12 May 1998', ' ') 
 '12'

substring-before('12 May 1998', '-') 
 ''

Функция substring-after

string substring-after(string, string)

Эта функция аналогична функции

substring-before
, только она возвращает строку, которая следует за вторым аргументом. Если вторая строка не содержится в первой, эта функция также вернет пустую строку.

Примеры

substring-after('12-May-1998', '-') 
 'May-1998'

substring-after('12 May 1998', ' ') 
 'May 1998'

substring-after('12 May 1998', ' ') 
 'May 1998'

substring-after('12 May 1998', '-') 
 ''

Функция substring

string substring(string, number, number?)

Функция

substring
возвращает подстроку переданного ей строкового аргумента, которая начинается с позиции, указанной вторым аргументом и длиной, указанной третьим аргументом. Если третий аргумент опущен, подстрока продолжается до конца строки. Если численные аргументы являются нецелыми, они округляются при помощи функции
round
.

В XPath позицией первого символа является

1
, а не
0
, как в некоторых других языках программирования.

При вычислении подстроки учитываются следующие условия.

□ Если первый численный аргумент меньше

1
(это относится и к отрицательной бесконечности), то подстрока начинается с начала строки.

□ Если первый численный аргумент больше длины строки (это относится и к положительной бесконечности), то подстрока будет пустой.

□ Если второй численный аргумент меньше

1
(это относится и к отрицательной бесконечности), то подстрока будет пустой.

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

Примеры

substring('123456', 2, 3) 
 '234'

substring('123456', 2, 5) 
 '23456'

substring('123456', 2, 6) 
 '23456'

substring('123456', 2) 
 '23456'

substring('123456', -4) 
 '123456'

substring('123456', 5, 5) 
 '5'

substring('123456', 5) 
 '56'

substring ('123456', 6) 
 '6'

substring('123456', 1 div 0, ) 
 ''

substring('123456', 2, -1) 
 ''

Функция string-length

number string-length(string?)

Функция

string-length
возвращает число символов строкового аргумента. Если аргумент опущен,
string-length
возвращает длину строкового представления контекстного узла.

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

Примеры

string-length('Barnes and Noble') 
16

string-length('Barness#x20;& Noble') 
 14

Функция normalize-space

string normalize-space(string?)

Функция

normalize-space
производит со своим строковым аргументом так называемую нормализацию пробельного пространства. Это означает, что в строке удаляются ведущие и заключающие пробельные символы, а все последовательности пробелов заменяются одним пробельным символом. Иными словами, функция удаляет "лишние" пробелы в строке.

Если аргумент функции опущен, она выполняется со строковым значением контекстного узла.

Примеры

normalize-space(' А - В - С ') 
 'А-В-С'

normalize-space('А &#х9; В &#х9; С') > 'A B C'

Функция translate

string translate(string, string, string)

Функция

translate
производит замену символов первого своего строкового аргумента, которые присутствуют во втором аргументе на соответствующие символы третьего аргумента.

Пример

translate('abcdefgh', 'aceg', 'ACEG') 
 'AbCdEfGh'

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

Пример

translate('abcdefgh', 'acaeaga', 'ACBECGD') 
 'AbCdEfGh'

Если второй аргумент длиннее третьего, символы, для которых нет соответствующей замены, удаляются из строки.

Пример

translate('a b-c=d+e|f/g\h', 'aceg-=+|/\', 'ACEG') 
 'AbCdEfGh'

Если третий аргумент длиннее второго, остаток строки игнорируется.

Пример

translate('abcdefgh', 'aceg', 'ACEGBDFH') 
 ' AbCdEfGh'

Функцию

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

Пример

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

lowercase
и
uppercase
, которые будут содержать строчные и прописные символы основного русского алфавита (мы включили в него букву ё — строчную ("
ё
") и прописную ("
Ё
"), хотя в соответствии с Unicode она относится к расширениям). Мы также создадим два именованных шаблона, которые будут менять регистр символов строкового параметра
str
. Для удобства использования мы вынесем определения переменных и шаблонов во внешний модуль
ru.xsl
.

Листинг 6.6. Преобразование ru.xsl

 version="1.0"

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


  name="uppercase"

  select="concat('АБВГ',

                 'ДЕЁЖЗ',

                 'ИЙКЛ',

                 'МНОП',

                 'РСТУ',

                 'ФХЦЧ',

                 'ШЩЪЫ',

                 'ЬЭЮЯ')"/>


  name="lowercase"

  select="concat('абвг',

                 'деёжЗ',

                 'ийкл',

                 'мноп',

                 'рсту',

                 'фхцч',

                 'шщъы',

                 'ьэюя')"/>




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

xsl:include
или
xsl:import
. После этого в основном преобразовании будут доступны переменные
lowercase
и
uppercase
, которые можно будет использовать в функции
translate
и шаблоны с именами
lower
и
upper
.

Использовать функцию

translate
с переменными
lowercase
и
uppercase
можно следующим образом:

translate('Дом', $uppercase, $lowercase) 
 'дом'

translate('Дом', $lowercase, $uppercase) 
 'ДОМ'

Именованные шаблоны можно вызывать элементом

xsl:call-template
, передавая параметр при помощи
xsl:with-param
. Например, следующий фрагмент шаблона

...

>

...

создаст в выходящем дереве текстовый узел со значением "

дом
".

Функции множеств узлов

Функции last и position

number last()

number position()

Функция

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

Функция

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

Пример

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

 ...

где атрибут

name
будет содержать имя, a
position
— через дробь позицию элемента в контексте и размер контекста.

Листинг 6.7. Входящий документ

<а>

Листинг 6.8. Преобразование

 version="1.0"

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




Листинг 6.9. Выходящий документ

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

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

Функция count

number count(node-set)

Функция

count
возвращает число узлов, которое входит во множество, переданное ей в качестве аргумента.

Пример

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

count(/*/*)
. Например, для входящего документа из примера к функциям
last
и
position
(листинг 6.7) это выражение примет значение
3
.

Приведем несколько других примеров, используя тот же документ.

Покажем, что дерево документа на листинге 6.7 имеет ровно один корневой узел:

count(/) 
 1

и ровно один элемент, находящийся в корне:

count(/*) 
 1

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

a
(это те самые пробельные текстовые узлы, которые были удалены элементом
xsl:strip-space
):

count(/a/text()) 
 4

Подсчитаем общее количество элементов в документе:

count(//*) 
 6

Функции local-name, namespace-uri и name

string local-name(node-set?)

string namespace-uri(node-set?)

string name(node-set?)

Функция

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

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

□ Если аргументом является пустое множество, функция возвращает пустую строку.

□ Если первый в порядке просмотра документа узел переданного множества не имеет расширенного имени, функция возвращает пустую строку.

□ В противном случае функция возвращает локальную часть расширенного имени первого в порядке просмотра документа узла переданного множества.

Функция

namespace-uri
работает совершенно аналогично функции
local-name
за тем исключением, что возвращает не локальную часть расширенного имени, a URI пространства имен этого узла. Эта функция выполняется следующим образом.

□ Если аргумент опущен, его значением по умолчанию является множество, содержащее единственный контекстный узел.

□ Если аргументом является пустое множество, функция возвращает пустую строку.

□ Если первый в порядке просмотра документа узел переданного множества не имеет расширенного имени, функция возвращает пустую строку.

□ Если первый в порядке просмотра документа узел переданного множества не принадлежит никакому пространству имен, функция возвращает пустую строку.

□ В противном случае функция возвращает URI пространства имен первого в порядке просмотра документа узла переданного множества.

Функция

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

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

name
может использовать любой из них.

Пример

Для следующего элемента

 xmlns:a="http://www.a.com"

 xmlns:b="http://www.a.com"

 xmlns:c="http://www.a.com"/>

функция

name
может вернуть
a:body
,
b:body
или
c:body
.

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

Так же как

local-name
и
namespace-uri
, функция name имеет следующие особенности использования.

□ Если аргумент опущен, то его значением по умолчанию является множество, содержащее единственный контекстный узел.

□ Если аргументом является пустое множество, то функция возвращает пустую строку.

□ Если первый в порядке просмотра документа узел переданного множества не имеет расширенного имени, то функция возвращает пустую строку.

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

QName
, соответствующее расширенному имени первого в порядке просмотра документа узла переданного множества.

Пример

Мы можем видоизменить преобразование, приведенное в примере к функциям

last
и
position
(листинг 6.7), чтобы генерируемые элементы содержали информацию об имени, пространстве имен и локальной части имени элементов.

Листинг 6.10. Входящий документ

 xmlns:a="http://www.a.com"

 xmlns:b="http://www.b.com">

Листинг 6.11. Преобразование

 version="1.0"

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

 xmlns:a="http://www.a.com"

 xmlns:b="http://www.b.com">


   name="{name()}"

   namespace-uri="{namespace-uri()}"

   local-name="{local-name()}">


Листинг 6.12. Выходящий документ

 xmlns:a="http://www.a.com"

 xmlns:b="http://www.b.com"

 name="a:a"

 namespace-uri="http://www.a.com"

 local-name="a">

  namespace-uri="http://www.b.com" 

  local-name="b">

   namespace-uri=""

   local-name="c"/>

Функция id

node-set id(object)

Мы уже встречались с функцией

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

Функция

id
выполняется по-разному в зависимости от того, какой тип данных ей передается.

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

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

id
, примененной к строковому значению каждого из узлов множества.

□ Если аргументом функции является объект другого типа, аргумент вначале преобразуется в строку и функция

id
выполняется как со строковым параметром.

Пример

Предположим, что мы используем XML для того, чтобы описать граф — множество вершин, соединенных дугами (рис. 6.11).

Рис. 6.11. Изображение графа, который мы будем описывать в XML

Каждой вершине будет соответствовать элемент

vertex
. Для того чтобы описать все связи, мы дадим каждой вершине уникальное имя, которое будет записано в ее
ID
-атрибуте
name
. Имена вершин, с которыми она соединена, будут перечислены в атрибуте
connects
типа
IDREFS
.

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

Листинг 6.13. Документ gemini.xsl

Декларация типа документа вынесена во внешний файл

gemini.dtd
.

Листинг 6.14. Файл gemini.dtd

 name ID #REQUIRED

 connects IDREFS #REQUIRED>

При обработке этого документа функция

id
будет очень полезна для выбора элементов соединенных вершин. Действительно, функция
id
, которой будет передано значение атрибута
connects
(в котором через пробелы перечислены вершины, смежные данной), возвратит множество, состоящее из элементов с перечисленными уникальными идентификаторами. Так, например, функция
id('tau upsilon')
возвратит множество, состоящее из двух элементов с атрибутами name, равными
tau
и
upsilon
соответственно.

Более того, функция

id
может быть вычислена и от множества узлов. В этом случае ее значением будет объединение множеств, полученных в результате выполнения функции от строкового значения каждого узла переданного множества. Например,
id(id('tau upsilon')/@connects)
возвратит множество, состоящее из вершин с именами
alpha
,
beta
,
delta
,
epsilon
,
theta
,
iota
и
kappa
— множество вершин, смежных с вершинами tau и upsilon.

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

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

Для того чтобы найти множество вершин, достижимых за один шаг (иначе говоря, смежных), мы воспользуемся выражением вида

id(@connects)
, для выборки множества вершин, достижимых из текущей за два шага — выражением
id(id(@connects)/@connects)
. Таким образом, множество вершин, достижимых не более чем за два шага, будет вычисляться как

id(@connects)|id(id(@connects)/@connects)

Листинг 6.15. Преобразование gemini.xsl

 version="1.0"

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





Листинг 6.16. Выходящий документ

Базовые продукции XPath