Алексей Валиков - Технология XSLT
Таблица 8.2. Значения атрибута source элементов item
Идентификатор (значение атрибута source) Элемент item a <item source="a" name="A"/> a <item source="a" name="C"/> a <item source="a" name="H"/> b <item source="b" name="B"/> b <item source="b" name="E"/> b <item source="b" name="F"/> с <item source="c" name="D"/> с <item source="c" name="G"/>Таким образом, значение "с" идентифицирует объекты с именами D и G, а значение "а" — объекты с именами A, C и H, причем находить соответствующие элементы в таблице по их ключевому свойству не составляет никакого труда.
Несмотря на то, что произведенные нами манипуляции чрезвычайно просты (и настолько же эффективны), процессор вряд ли в общем случае сможет сделать что-либо подобное сам, и потому очень важной является возможность явным образом выделять в XSLT-преобразованиях ключевые свойства множеств узлов.
В этом разделе мы будем рассматривать две конструкции, позволяющие манипулировать множествами узлов посредством ключей — это элемент xsl:key, который определяет в преобразовании именованный ключ, и функция key, которая возвращает множество узлов, идентифицирующихся заданными значениями ключей.
Элемент xsl:key
Синтаксис элемента несложен:
<xsl:key
name="имя"
match="паттерн"
use="выражение"/>
Элемент верхнего уровня xsl:key определяет в преобразовании ключ именем, заданным в значении атрибута name, значением которого для каждого узла документа, соответствующего паттерну match, будет результат вычисления выражения, заданного в атрибуте use. Ни атрибут use, ни атрибут match не могут содержать переменных.
ПримерВ нашем примере элементы item идентифицируются значениями своих атрибутов source. Для их идентификации мы можем определить ключ с именем src следующим образом:
<xsl:key name="src" match="item" use="@source"/>
Следуя строгому определению, данному в спецификации языка, ключом называется тройка вида (node, name, value), где node — узел, name — имя и value — строковое значение ключа. Тогда элементы xsl:key, включенные в преобразование, определяют множество всевозможных ключей обрабатываемого документа. Если этому множеству принадлежит ключ, состоящий из узла x, имени у и значения z, говорят, что узел x имеет ключ с именем у и значением z или что ключ у узла x равен z.
ПримерКлюч src из предыдущего примера определяет множество, которое состоит из следующих троек:
(<item name="A".../>, 'src', 'a')
(<item name="B".../>, 'src', 'b')
(<item name="C".../>, 'src', 'a')
(<item name="D".../>, 'src', 'c')
...
(<item name="H".../>, 'src', 'a')
В соответствии с нашими определениями мы можем сказать, что элемент
<item source="b" name="B"/>
имеет ключ с именем "src" и значением "b" или что ключ "src" элемента
<item source="a" name="H"/>
равен "a".
Для того чтобы обращаться к множествам узлов по значениям их ключей, в XSLT существует функция key, о которой мы сейчас и поговорим.
Функция key
Ниже приведена синтаксическая конструкция данной функции:
node-set key(string, object)
Итак, элементы xsl:key нашего преобразования определили множество троек (node, name, value). Функция key(key-name, key-value) выбирает все узлы x такие, что значение их ключа с именем key-name (первым аргументом функции) равно key-value (второму аргументу функции).
ПримерЗначением выражения key('src', 'a') будет множество элементов item таких, что значение их ключа "src" будет равно "а". Попросту говоря, это будет множество объектов источника "а".
Концепция ключей довольно проста, и существует великое множество аналогий в других языках программирования: от хэш-функций до ключей в реляционных таблицах баз данных. По всей вероятности, читателю уже встречалось что-либо подобное.
Но не следует забывать, что язык XSLT — довольно нетрадиционный язык и с точки зрения синтаксиса, и с точки зрения модели данных. Как следствие, ключи в нем имеют довольно много скрытых нюансов, которые очень полезно знать и понимать. Мы попытаемся как можно более полно раскрыть все эти особенности.
Определение множества ключей
Не представляет особой сложности определение множества ключей в случае, если в определении они идентифицируются строковыми выражениями. Например, в следующем определении
<xsl:key name="src" match="item" use="string(@source)"/>
атрибут use показывает, что значением ключа src элемента item будет значение атрибута source. Но что можно сказать о следующем определении:
<xsl:key name="src" match="item" use="@*"/>
Очевидно, это уже гораздо более сложный, но, тем не менее, вполне реальный случай, не вписывающийся в определения, которые давались до сих пор. Мы говорили лишь о том, что множество ключей определяется элементами 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')