Но не следует забывать, что язык XSLT — довольно нетрадиционный язык и с точки зрения синтаксиса, и с точки зрения модели данных. Как следствие, ключи в нем имеют довольно много скрытых нюансов, которые очень полезно знать и понимать. Мы попытаемся как можно более полно раскрыть все эти особенности.
Определение множества ключей
Не представляет особой сложности определение множества ключей в случае, если в определении они идентифицируются строковыми выражениями. Например, в следующем определении
Очевидно, это уже гораздо более сложный, но, тем не менее, вполне реальный случай, не вписывающийся в определения, которые давались до сих пор. Мы говорили лишь о том, что множество ключей определяется элементами
xsl:key
преобразования, но как именно оно определяется — оставалось доселе загадкой. Восполним этот пробел, дав строгое определение множеству ключей.
Узел
x
обладает ключом с именем
у
и строковым значением
z
тогда и только тогда, когда в преобразовании существует элемент
xsl:key
такой, что одновременно выполняются все нижеперечисленные условия:
□ узел
x
соответствует паттерну, указанному в его атрибуте
match
;
□ значение его атрибута
name
равно имени
y
;
□ результат
u
вычисления выражения, указанного в значении атрибута
use
в контексте текущего множества, состоящего из единственного узла
x
, удовлетворяет одному из следующих условий:
•
u
является множеством узлов и
z
равно одному из их строковых значений;
•
u
не является множеством узлов и
z
равно его строковому значению.
Без сомнения, определение не из простых. Но как бы мы действовали, если бы физически создавали в памяти множество ключей? Ниже представлен один из возможных алгоритмов:
□ для каждого элемента
xsl:key
найти множество узлов документа, удовлетворяющих его паттерну
match
(множество
X
);
□ для каждого из найденных узлов (
x
∈
X
) вычислить значение выражения атрибута
use
(значение
u(x)
);
□ если
u(x)
является множеством узлов (назовем его
Uх
), то для каждого
uxi
∈
Uх
создать ключ
(x, n, string(uxi))
, где
n
— имя ключа (значение атрибута
name
элемента
xsl:key
);
□ если
u(x)
является объектом другого типа (назовем его
ux
), создать ключ
(x, n, string(ux))
.
Пример
Найдем
множество ключей, создаваемое определением
<xsl:key name="src" match="item" use="@*"/>
Имена всех ключей будут одинаковы и равны
"src"
. Множество
x
узлов, удовлетворяющих паттерну
item
, будет содержать все элементы
item
обрабатываемого документа. Значением выражения, заданного в атрибуте use, будет множество всех узлов атрибутов каждого из элементов
item
. Таким образом, множество узлов будет иметь следующий вид:
(<item name="А".../>, 'src', 'a')
(<item name="А".../>, 'src', 'A')
(<item name="В".../>, 'src', 'b')
(<item name="В".../>, 'src', 'В')
(<item name="С".../>, 'src', 'а')
(<item name="С".../>, 'src', 'С')
(<item name="D".../>, 'src', 'с')
(<item name="D".../>, 'src', 'D')
...
(<item name="H".../>, 'src', 'a')
(<item name="H".../>, 'src', 'H')
В итоге функция
key('src', 'a')
будет возвращать объекты с именами
A
,
C
и
H
, а функция
key('src', 'A')
— единственный объект с именем
A
(поскольку ни у какого другого элемента
item
нет атрибута со значением
"A"
).
Необходимо сделать следующее замечание: совершенно необязательно, чтобы процессор действительно физически создавал в памяти множества ключей. Это множество определяется чисто логически — чтобы было ясно, что же все-таки будет возвращать функция
key
. Процессоры могут вычислять значения ключей и искать узлы в документе и во время выполнения, не генерируя ничего заранее. Но большинство процессоров, как правило, все же создают в памяти определенные структуры для манипуляций с ключами. Это могут быть хэш-таблицы, списки, простые массивы или более сложные нелинейные структуры, упрощающие поиск, — важно другое. Важно то, что имея явное определение ключа в
xsl:key
, процессор может производить такую оптимизацию.
Использование нескольких ключей в одном преобразовании
В случае, когда к узлам в преобразовании нужно обращаться по значениям различных свойств, можно определить несколько ключей — каждый со своим именем. Например, если мы хотим в одном случае обращаться к объектам, принадлежащим одному источнику, а во втором — к объектам с определенными именами, мы можем определить в документе два ключа — один с именем