Алексей Валиков - Технология XSLT
(<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, процессор может производить такую оптимизацию.
Использование нескольких ключей в одном преобразовании
В случае, когда к узлам в преобразовании нужно обращаться по значениям различных свойств, можно определить несколько ключей — каждый со своим именем. Например, если мы хотим в одном случае обращаться к объектам, принадлежащим одному источнику, а во втором — к объектам с определенными именами, мы можем определить в документе два ключа — один с именем src, второй — с именем name:
<xsl:key name="src" match="item" use="@source"/>
<xsl:key name="name" match="item" use="@name"/>
Множество ключей, созданных этими двумя определениями, будет выглядеть следующим образом:
(<item name="А".../>, 'src', 'а')
(<item name="А".../>, 'name', 'А')
(<item name="В".../>, 'src', 'b')
(<item name="В".../>, 'name', 'В')
(<item name="C".../>, 'src', 'a')
(<item name="C".../>, 'name', 'С')
(<item name="D".../>, 'src', 'с')
(<item name="D".../>, 'name', 'D')
...
(<item name="H".../>, 'src', 'a')
(<item name="H".../>, 'name', 'H')
В этом случае функция key('src', 'а') возвратит объекты с именами A, C и H, а функция key('name', 'а') — объект с именем А.
Имя ключа является расширенным именем. Оно может иметь объявленный префикс пространства имен, например
<xsl:key
name="data:src"
match="item"
use="@source"
xmlns:data="urn:user-data"/>
В этом случае функция key(key-name, key-value) будет возвращать узлы, значение ключа с расширенным именем key-name которых равно key-value. Совпадение расширенных имен определяется как обычно — по совпадению локальных частей и URI пространств имен.
Использование нескольких определений одного ключа
Процессор должен учитывать все определения ключей данного преобразования — даже если некоторые из них находятся во включенных или импортированных модулях. Порядок импорта элементов xsl:key не имеет значения: дело в том, что определения ключей с одинаковыми именами для одних и тех же узлов, но с разными значениями ключа не переопределяют, а дополняют друг друга.
ПримерПредположим, что в нашем документе имеется несколько элементов item, в которых не указано значение атрибута source, но по умолчанию мы будем причислять их к источнику а. Соответствующие ключи будут определяться следующим образом:
<xsl:key name="src" match="item[@source]" use="@source"/>
<xsl:key name="src" match="item[not(@source)]" use="'a'"/>
To есть для тех элементов item, у которых есть атрибут source, значением ключа будет значение этого атрибута, для тех же элементов, у которых атрибута source нет, его значением будет "а".
Для входящего документа вида
<items>
<item source="a" name="A"/>
<item source="b" name="B"/>
<item source="a" name="C"/>
<item source="c" name="D"/>
...
<item source="a" name="H"/>
<item name="I"/>
<item name="J"/>
<item name="K"/>
</items>
соответствующее множество ключей будет определяться следующим образом:
(<item name="А".../>, 'src', 'а')
(<item name="В".../>, 'src', 'b')
(<item name="С".../>, 'src', 'а')
(<item name="D".../>, 'src', 'c')
...
(<item name="H".../>, 'src', 'a')
(<item name="I".../>, 'src', 'a')
(<item name="J".../>, 'src', 'a')
(<item name="K".../>, 'src', 'a')
Функция key('src', 'a') возвратит объекты с именами A, C, H, I, J и K.
То, что одни и те же узлы могут иметь разные значения одного ключа, является также очень удобным свойством. Например, два определения ключей, приведенные выше, можно дополнить третьим:
<xsl:key name="src" match="item[not(@source)]" use="'#default'"/>
Это определение позволит по значению "#default" обращаться к объектам, принадлежащим источнику по умолчанию.
Использование множеств узлов в функции key
Функция key принимает на вход два аргумента: первым аргументом является строка, задающая имя ключа, в то время как вторым аргументом может быть объект любого типа. В том случае, если аргумент key-value в функции key(key-name, key-value) является множеством узлов, функция key возвратит все узлы, имеющие ключ key-name со значением, равным хотя бы одному из строковых значений узла множества key-value.
ПримерПредположим, что источники объектов будут сгруппированы следующим образом:
<sources>
<source name="a"/>
<source name="c"/>
</source>
Для того чтобы вычислить множество элементов item, принадлежащих любому из источников данной группы, достаточно будет воспользоваться выражением вида
key('src', sources/source/@name)
Множество узлов, выбираемое путем sources/source/@name, будет содержать атрибуты name элементов source. Их строковые значения будут равны а и с, значит, наше выражение возвратит множество элементов item, значение атрибута source которых равно либо а либо с.
Использование ключей в нескольких документах
Ключи, определенные в преобразовании, могут использоваться для выбора узлов в различных обрабатываемых документах. Функция key возвращает узлы, которые принадлежат текущему документу, то есть документу, содержащему текущий узел. Значит, для того, чтобы выбирать узлы из внешнего документа, необходимо сделать текущим узлом один из узлов этого внешнего документа. Контекстный документ может быть легко изменен элементом xsl:for-each, например, для того, чтобы текущим документом стал документ a.xml, достаточно написать
<xsl:for-each select="document('а.xml')">
<!-- Теперь текущим документом стал документ а.xml -->
</xsl:for-each>
ПримерПредположим, что нам нужно выбрать объекты, принадлежащие источнику a, причем принадлежность объектов определена в двух внешних документах, a.xml и b.xml.
Листинг 8.22. Входящий документ<source name="a"/>
Листинг 8.23. Документ a.xml<items>
<item source="a" name="A"/>
<item source="b" name="B"/>
<item source="a" name="C"/>
<item source="c" name="D"/>
</items>
Листинг 8.24. Документ b.xml<items>
<item source="b" name="E"/>
<item source="b" name="F"/>
<item source="c" name="G"/>
<item source="a" name="H"/>
</items>
Листинг 8.25. Преобразование<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="src" match="item" use="@source"/>
<xsl:template match="source">
<xsl:variable name="name" select="@name"/>
<xsl:copy>
<xsl:for-each select="document('a.xml')|document('b.xml')">
<xsl:copy-of select="key('src', $name)"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Листинг 8.26. Выходящий документ<source>
<item source="a" name="A"/>
<item source="a" name="C"/>
<item source="a" name="H"/>
</source>