Книга: Технология XSLT
Ключи
Разделы на этой странице:
Прежде чем мы приступим к разбору ключей, которые являются одной из самых мощных концепций языка XSLT, попробуем решить одну несложную задачку.
Листинг 8.19. Входящий документ
<items>
<item source="a" name="A"/>
<item source="b" name="B"/>
<item source="a" name="C"/>
<item source="c" name="D"/>
<item source="b" name="E"/>
<item source="b" name="F"/>
<item source="c" name="G"/>
<item source="a" name="H"/>
</items>
Пусть входящий документ представляет собой список объектов (элементов item
), каждый из которых имеет имя (атрибут name
) и источник (атрибут source
). Требуется сгруппировать объекты по своим источникам и получить документ приблизительно следующего вида.
Листинг 8.20. Требуемый результат
<sources>
<source name="a">
<item source="a" name="A"/>
<item source="a" name="C"/>
<item source="a" name="H"/>
</source>
<source name="b">
<item source="b" name="B"/>
<item source="b" name="E"/>
<item source="b" name="F"/>
</source>
<source name="c">
<item source="c" name="D"/>
<item source="c" name="G"/>
</source>
</sources>
Первым шагом на пути решения этой задачи является формулировка в терминах XSLT предложения "сгруппировать объекты по своим источникам". Источник каждого объекта определяется его атрибутом source
, значит множество объектов, принадлежащих одному источнику "а"
, будет определяться путем выборки
/items/item[@source='a']
Тогда для каждого элемента item
в его группу войдут элементы, которые будут выбраны выражением
/items/item[@source=current()/@source]
Попробуем использовать этот факт в следующем шаблоне:
<xsl:template match="item">
<source name="{@source}">
<xsl:copy-of select="/items/item[@source=current()/@source]"/>
</source>
</xsl:template>
Как и ожидалось, при применении этого правила к элементам item
для каждого из них будет создана группа, принадлежащая тому же источнику, — уже хороший результат, но в условии требуется создать по группе не для каждого объекта, а для каждого источника. Чтобы достичь этого, можно создавать группу только для первого объекта, принадлежащего ей. Провести такую проверку опять же несложно: объект будет первым в группе тогда и только тогда, когда ему не предшествуют другие, элементы item
, принадлежащие тому же источнику. Иначе говоря, создаем группы только для тех элементов, для которых выражение
preceding-sibling::item[@source-current()/@source]
будет возвращать пустое множество.
С небольшими добавлениями искомое преобразование целиком будет иметь вид.
Листинг 8.21. Преобразование
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="items">
<sources>
<xsl:apply-templates/>
</sources>
</xsl:template>
<xsl:template match="item">
<xsl:choose>
<xsl:when
test="preceding-sibling::item[@source=current()/@source]"/>
<xsl:otherwise>
<source name="{@source}">
<xsl:copy-of select="self::node()
|following-sibling::item[@source=current()/@source]"/>
</source>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Бесспорно, решение было несложным, но довольно громоздким. Самым же узким местом в этом преобразовании является обращение к элементам item
источника текущего элемента посредством сравнения атрибутов source
.
Проблема совершенно стандартна для многих преобразований: нужно выбирать узлы по определенным признакам, причем делать это нужно как можно более эффективно. Хорошо, что в нашем документе было всего восемь элементов item
, но представьте себе ситуацию, когда элементов действительно много.
Проблема, которую мы подняли, достаточно серьезна. Она состоит в оптимизации поиска узлов с определенными свойствами в древовидно организованной структуре.
Попробуем разобраться в смысле фразы "узел обладает определенными свойствами". Очевидно, это означает, что для этого узла выполняется некое логическое условие, иначе говоря, некий предикат обращается в "истину".
Однако какого именно типа условия мы чаще всего проверяем? Анализируя различные классы задач, можно придти к выводу, что в большинстве случаев предикаты являются равенствами — выражениями, которые обращаются в "истину" тогда и только тогда, когда некоторый параметр узла, не зависящий от текущего контекста, равен определенному значению. В нашем примере смысл предиката на самом деле состоит не в том, чтобы проверить на истинность выражение @source=current()/@source
, а в том, чтобы проверить на равенство @source
и current()/@source
.
Если переформулировать это для общего случая, то нам нужно выбрать не те узлы, для которых истинно выражение A=B
, скорее нужно выбрать те, для которых значение A
равно значению B. Иначе говоря, узел будет идентифицироваться значением в своего свойства A
. И если мы заранее вычислим значения свойств A
, проблема поиска узлов в дереве сведется к классической проблеме поиска элементов множества (в нашем случае — узлов дерева) по определенным значениям ключей (в нашем случае — значениями свойств A
).
Чтобы пояснить это, вернемся к нашему примеру: мы ищем элементы item
со значением атрибута source
, равным заданному. Свойством, идентифицирующим эти элементы, в данном случае будут значения их атрибутов source
, которые мы можем заранее вычислить и включить в табл. 8.2.
Таблица 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
, которая возвращает множество узлов, идентифицирующихся заданными значениями ключей.
- 2.2.5. Ключи
- От исключительной полезности к стратегическому ценообразованию
- Как подключить ноутбук к сети Интернет
- Могу ли я изменить или отключить звуки, которые проигрываются при запуске Windows, щелчке кнопкой мыши на папке и т. д.?
- Как записать звук и включить его в звуковую схему Windows?
- Из компьютера иногда раздается резкий звук. Как его отключить?
- Меня подключили к сети. Как в нее входить?
- Как отключить сеть, не выдергивая кабель?
- На сайтах я читаю только текст и не хочу платить за картинки. Как их отключить?
- При запуске Internet Explorer загружается какая-то непонятная страница. Как ее отключить?
- При выключении не дождался конца процесса и отключил питание. Теперь при запуске компьютер начинает бесконечно обращатьс...
- Возле часов постоянно отображается значок Безопасное извлечение устройства, который предлагает отключить жесткий диск и ...