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

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

Выражениям языка соответствует нетерминал

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

Базовая конструкция, использующаяся в выражениях, называется первичным выражением (от англ. primary expression). Первичные выражения могут быть переменными, литералами, числами, вызовами функций, а также обычными выражениями Expr, сгруппированными в круглых скобках:

[XP15] PrimaryExpr ::= VariableReference

                       | '(' Expr ')'

                       | Literal

                       | Number

                       | FunctionCall

Переменные

Переменные вызываются в выражениях XPath по своему имени, которому предшествует символ "

$
". Например, если мы объявили переменную
nodes
:

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

$nodes
.

Переменные, так же как элементы и атрибуты XML, могут иметь расширенные имена вида

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

Пример

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

 version="1.0"

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

 xmlns:a="uri:a"

 xmlns:b="uri:b">


 ...

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

а
, будет содержаться в переменной
a:elementcount
, а пространству имен
b
— в переменной
b:elementcount
.

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


будет корректен, в шаблоне


процессор не сможет найти объявление переменной

$elementcount
, потому что расширенное имя объявленной переменной состоит из URI пространства имен
"uri:а"
и локальной части имени
elementcount
, а расширенное имя переменной
elementcount
состоит из нулевого URI и локальной части
elementcount
. Иными словами, эти переменные принадлежат разным пространствам.

Операции с булевыми значениями

XPath поддерживает только две логические операции —

and
(логическое "и") и
or
(логическое "или"). В XPath нет оператора логического отрицания, вместо него применяется функция
not
, которая возвращает "истину", если аргументом была "ложь" и наоборот.

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

Логические вычисления в XPath абсолютно стандартны. Мы приведем их для справки в табл. 6.2.


Таблица 6.2. Вычисление логических выражений

ЗначениеВыражение
A
B
A or В
A and В
false
false
false
false
false
true
true
false
true
false
true
false
true
true
true
true

Как и во многих других языках, операция "и" (

and
) старше операции "или" (
or
). Например, такое выражение, как
A and B or C and D or E
эквивалентно выражению
(A and В) or (С and D) or E
.

Приведем синтаксические правила продукций логических операций XPath. Операции "или" соответствует продукция

OrExpr
, операции "и" — продукция
AndExpr
.

[XP21] OrExpr  ::= AndExpr | OrExpr 'or' AndExpr

[XP22] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr

Операции с числами

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

Арифметические операции

Арифметические операции XPath сведены в табл. 6.3.


Таблица 6.3. Арифметические операции в XPath-выражениях

ОперацияСинтаксис
Сложение
A + B
Вычитание
A - B
Умножение
A * B
Деление
A div B
Остаток деления
A mod B
Унарное отрицание
- A

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

'2' * '2' 
 4

Арифметические действия в XPath работают, что называется, "как обычно", то есть совершенно стандартным образом. Арифметика XPath основывается на стандарте IEEE 754, который был использован и в других распространенных языках программирования, например в Java. Пожалуй, следует лишь обратить внимание на операторы деления, поскольку в разных языках они означают разные действия и потому легко запутаться.

Оператор

div
делит свой первый операнд на второй. Это не целочисленное деление, как в некоторых других языках,
div
осуществляет деление чисел с плавающей точкой. Оператор
div
аналогичен оператору деления "
/
" в таких языках, как Java, С++ и Pascal.

Примеры:

3.2 div 2.5 
 1.28

3.2 div -2.5 
 -1.28

-3.2 div -2.5 
 1.28

Оператор

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

3.2 mod 2 
 1.2

3.2 mod -2 
 1.2

-3.2 mod 2 
 -1.2

-3.2 mod -2 
 -1.2

Оператор mod аналогичен оператору "

%
" в таких языках, как Java и ECMAScript.

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

A
можно нацело разделить на число
B
выражением
(A - (A mod B)) div B
.

Пример:

(3.2 - (3.2 mod 2)) div 2 
 1

Во избежание ошибок следует аккуратно использовать знак вычитания в арифметических операциях. Дело в том, что синтаксис XML разрешает использовать символ "

-
" в именах элементов, которые в свою очередь также могут быть использованы в выражениях в составе путей выборки. Например,
A - B
означает разность
A
и
B
, в то время как
A-B
будет воспринято, как имя '
A-B
'. Поэтому рекомендуется выделять знак минуса пробелами.

Приведем продукции выражений с арифметическими операциями.

Унарному отрицанию соответствует продукция

UnaryExpr
. Как можно видеть, в текущей версии языка это — единственная унарная операция (то есть операция одного элемента).

[XP27] UnaryExpr ::= UnionExpr | '-' UnaryExpr

Попробуем упростить это правило, раскрыв рекурсию

UnaryExpr ::= '-' * UnionExpr

Таким образом, унарное отрицание можно повторять несколько раз:

------5 
 5

Умножению, делению и вычислению остатка деления соответствует одна продукция

MultiplicativeExpr
:

[XP26] MultiplicativeExpr ::= UnaryExpr

                           | MultiplicativeExpr MultiplyOperator UnaryExpr

                              | MultiplicativeExpr 'div' UnaryExpr

                              | MultiplicativeExpr 'mod' UnaryExpr

Оператор умножения вынесен в отдельное правило:

[XP34] MultiplyOperator ::= '*'

Сложению и вычитанию соответствует правило

AdditiveExpr
:

[XP25] AdditiveExpr ::= MultiplicativeExpr

                        | AdditiveExpr '+' MultiplicativeExpr

                        | AdditiveExpr '-' MultiplicativeExpr

Операции сравнения

XPath позволяет сравнивать числа при помощи операторов, перечисленных в табл. 6.4.


Таблица 6.4. Операторы сравнения

ОператорЗначение
=
Равно
!=
Не равно
<
Меньше
>
Больше
<=
Меньше или равно (не больше)
>=
Больше или равно (не меньше)

XPath-выражения чаще всего используются в значениях атрибутов, символ "

<
" в которых в соответствии с синтаксисом XML использовать нельзя. Поэтому операторы сравнения "меньше" ("
<
") и "не больше" ("
<=
") нужно записывать с использованием сущностей. Оператор "меньше" может быть записан как "
<
;", а оператор "не больше" как "
<=
".

Пример

Результатом обработки элемента

будет строка "

true
".

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

В XPath вполне корректным будет выражение вида

A > B > C
. Однако результат его будет довольно неожиданным. В XPath действует правило левой ассоциативности операторов сравнения, поэтому
A > B > C
будет равносильно
(А > B) > C
. То есть
A
будет сравнено с
B
, затем результат, истина или ложь, будет преобразован в числовой тип (получится
1
или
0
) и затем сравнен со значением
C
.

Пример:

3 > 2 > 1 
 (3 > 2) > 1 
 1 > 1 
 false

3 > 2 > 0 
 (3 > 2) > 0 
 1 > 0 
 true

Неравенствам в XPath соответствует продукция

RelationalExpr
:

[XP24] RelationalExpr ::= AdditiveExpr

                          | RelationalExpr '<' AdditiveExpr

                          | RelationalExpr '>' AdditiveExpr

                          | RelationalExpr '<=' AdditiveExpr

                          | RelationalExpr '>=' AdditiveExpr

Операции "равно" и "не равно" записываются при помощи продукции

EqualityExpr:

[XP23] EqualityExpr ::= RelationalExpr

                        | EqualityExpr '=' RelationalExpr

                        | EqualityExpr '!=' RelationalExpr

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

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

Фильтрация

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

В выражениях множества узлов могут также фильтроваться одним или несколькими предикатами. Узел остается в фильтруемом множестве, только если он удовлетворяет всем предикатам поочередно.

Пример

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

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

$nodes[self::а][@href]

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

nodes
и возвращаемого путем выборки
body/*
:

($nodes|body/*)[self::a][@href]

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

FilterExpr
:

[XP20] FilterExpr ::= PrimaryExpr | FilterExpr Predicate

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

FilterExpr ::= PrimaryExpr Predicate*

Выражение

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

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

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

$nodes/*

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

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

$nodes//node()

Здесь

node()
— это тест узла, выполняющийся для всех типов узлов, а
//
, как и обычно, сокращение от
/descendant-or-self:node()/
.

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

PathExpr
:

[XP19] PathExpr ::= LocationPath

                    | FilterExpr

                    | FilterExpr '/' RelativeLocationPath

                    | FilterExpr '//' RelativeLocationPath

Объединение множеств

Множества могут быть объединены при помощи оператора "

|
". В объединение будут входить узлы, которые присутствуют хотя бы в одном из множеств, причем результат не содержит повторений. Объединять можно любые выражения, результатом вычисления которых являются множества узлов.

Пример

Множество всех элементов

а
,
b
и
с
документа может быть задано выражением
//a|//b|//c
.

Выражению объединения соответствует продукция

UnionExpr
:

[XP18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr

Старшинство операций

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

Выражению, как самой общей конструкции XPath, соответствует продукция

Expr
, которая определяется следующим образом:

[XP14] Expr ::= OrExpr

То есть, фактически, выражение в XPath определяется через логическое выражение. Естественно, выражения не обязаны быть логическими. Просто в иерархии синтаксических правил логическое выражение "или" находится выше всего. Верхние правила определяются через более примитивные правила и так далее. В итоге иерархия выражений выстраивается следующим образом (в скобках приведены названия EBNF-правил):

□ выражения (

Expr
);

□ логические выражения "или" (

OrExpr
);

□ логические выражения "и" (

AndExpr
);

□ выражения равенства и неравенства (

EqualityExpr
);

□ выражения сравнения (

RelationalExpr
);

□ выражения сложения и вычитания (

AdditiveExpr
);

□ выражения умножения и деления (

MultiplicativeExpr
);

□ унарные выражения (

UnaryExpr
);

□ выражения объединения множеств (

UnionExpr
);

□ выражения путей выборки (

PathExpr
);

□ пути выборки (

LocationPath
), фильтрация множеств (
FilterExpr
), относительные пути выборки (
RelativeLocationPath
).

По этой схеме несложно выяснить старшинство операций — чем ниже выражение находится в этой иерархии, тем выше его приоритет. Для полной ясности, перечислим операции в порядке старшинства от старших, с большим приоритетом, к младшим, с меньшим приоритетом выполнения:

□ операции с путями выборки;

□ операция объединения множеств (

|
);

□ унарная операция отрицания (

-
);

□ умножение, деление и вычисление остатка от деления (

*
,
div
и
mod
);

□ операции сложения и вычитания (

+
и
-
);

□ операции сравнения (

<
,
>
,
<=
,
=>
);

□ операции проверки равенства и неравенства (

=
и
!=
);

□ операция "и" (

and
);

□ операция "или" (

or
).

Операции одного порядка имеют левую ассоциативность, как это было показано на примере с операциями сравнения (

3 > 2 > 1
равносильно
(3 > 2) >1
).

Функции