Книга: Технология XSLT
Разрешение конфликтов в шаблонах
Разделы на этой странице:
Как правило, каждое преобразование в XSLT определяет, включает или импортирует множество шаблонов, которые обрабатывают указанные части документов. При этом один и тот же узел документа может соответствовать нескольким шаблонным правилам. К примеру, элемент content
может быть обработан любым из следующих трех шаблонов.
Листинг 5.14. Конфликтующие шаблоны
<xsl:template match="*">
<element/>
</xsl:template>
<xsl:template match="node()">
<node/>
</xsl:template>
<xsl:template match="content">
<content/>
</xsl:template>
Ситуация, когда для обработки узла может быть применено несколько правил, называется конфликтом шаблонов. Конфликты такого рода неизбежны практически в любом преобразовании, к примеру, большинство шаблонов будет вступать в конфликт со встроенными правилами преобразования.
Для того чтобы в конфликтной ситуации решить, какой именно из шаблонов должен быть применен к данному узлу, процессоры используют два простых правила.
? Шаблоны, имеющие младший порядок импорта, исключаются из рассмотрения. Иными словами, из множества правил, подходящих для обработки текущего узла, остаются только правила, имеющие самый старший порядок импорта.
? Из оставшегося множества выбирается шаблон с наивысшим приоритетом. Если таких шаблонов несколько, процессор может либо выдать ошибку, либо применить тот, который описан в преобразовании последним.
Во втором из этих двух правил, мы встретились с понятием приоритета шаблона. Приоритет шаблона это не что иное, как численное значение, которое может быть указано в атрибуте priority
элемента xsl:template
. В том случае, если значение этого атрибута не определено, приоритет шаблонного правила вычисляется следующим образом.
? Прежде всего, шаблон, который обрабатывает несколько альтернатив, перечисленных через знак "|
", будет рассматриваться как множество шаблонов, обрабатывающих каждую из возможностей. Например, шаблон с атрибутом match="b|bold|B"
будет рассматриваться как три одинаковых шаблона с атрибутами match="b"
, match="bold"
и match="B"
соответственно.
? Если паттерн состоит из имени (QName
) или конструкции processing-instruction(литерал)
, которым предшествует дескриптор оси дочернего узла или атрибута (ChildOrAttributeAxisSpecifier
), приоритет шаблона равен 0
. Такие паттерны могут иметь следующий вид:
• QName
или child::QName
— выбор дочерних элементов;
• @QName
или attribute::QName
— выбор атрибутов;
• processing-instruction(литерал)
или child::processing-instruction(литерал)
— именной выбор дочерних инструкций по обработке.
Примеры паттернов с приоритетом, равным 0
:
• content
— выбор дочернего элемента content
;
• fo:content
— выбор дочернего элемента content
с префиксом пространств имен fo
;
• child::processing-instruction('арр')
— выбор дочерних инструкций по обработке, которые имеют вид <?app содержимое?>
;
• @xsd:name
— выбор атрибута xsd:name
текущего узла;
• @select
— выбор атрибута select
текущего узла.
? Если паттерн состоит из конструкции NCName:*
, которой предшествует ChildOrAxisSpecifier
, приоритет шаблона будет равен -0.25
. Такие паттерны могут иметь следующий вид:
• префикс:*
или child::префикс:*
— выбор всех дочерних элементов в определенном пространстве имен;
• @префикс:*
или attribute::префикс:*
— выбор всех атрибутов в определенном пространстве имен.
Примеры паттернов с приоритетом, равным -0.25
:
• fo:*
— выбор всех дочерних элементов в пространстве имен с префиксом fo
;
• attribute::xsl:*
— выбор всех атрибутов текущего элемента, которые находятся в пространстве имен с префиксом xsl
.
? Если паттерн состоит из проверки узла (NodeTest
), которой предшествует ChildOrAttributeAxisSpecifier
, приоритет шаблона будет равен -0.5
. Паттерны такого рода будут выглядеть как:
• NodeTest
или child::NodeTest
— выбор всех дочерних узлов, соответствующих данной проверке;
• QNodeTest
или attribute::NodeTest
— выбор всех атрибутов, соответствующих данной проверке.
? Примеры паттернов с приоритетом, равным -0.5
:
• text()
— выбор дочерних текстовых узлов;
• child::comment()
— выбор дочерних комментариев;
• @*
— выбор всех атрибутов данного шаблона.
? Если ни одно из предыдущих условий не выполняется, приоритет шаблона равен 0.5
.
Для удобства использования составим таблицу (табл. 5.1) с приоритетами тех или иных паттернов.
Таблица 5.1. Приоритет паттернов
Вид паттерна | Приоритет |
---|---|
QName |
0 |
child::QName |
|
@QName |
|
attribute::QName |
|
processing-instruction(литерал) |
|
child::processing-instruction(литерал) |
|
префикс:* |
-0.25 |
child::префикс:* |
|
@префикс:* |
|
attribute::префикс:* |
|
NodeTest |
-0.5 |
child::NodeTest |
|
@NodeTest |
|
attribute::NodeTest |
|
Другие паттерны | 0.5 |
Несмотря на то, что вычислением приоритета преобразований занимается процессор, полезно понимать механизм этого вычисления для того, чтобы уметь предсказывать, как будет решен конфликт тех или иных шаблонов. Довольно часто в преобразованиях допускаются ошибки, связанные с приоритетом применения шаблонов.
Пример
Вычислим в качестве упражнения приоритеты шаблонов для следующего примера.
Листинг 5.15. Преобразование
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="a">
<xsl:strip-space elements="*"/>
<!-- Первый шаблон -->
<xsl:template match="a:b">
<xsl:message>
<xsl:text>1</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<!-- Второй шаблон -->
<xsl:template match="a:a/a:b">
<xsl:message>
<xsl:text>2</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<!-- Третий шаблон -->
<xsl:template match="a:*">
<xsl:message>
<xsl:text>3</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<!-- Четвертый шаблон -->
<xsl:template match="node()">
<xsl:message>
<xsl:text>4</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<!-- Пятый шаблон -->
<xsl:template match="b">
<xsl:message>
<xsl:text>5</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="print-name">
<xsl:text> template matched </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>.</xsl:text>
</xsl:template>
</xsl:stylesheet>
Пять шаблонов этого преобразования могут соответствовать одним и тем же узлам, а значит, создавать множество конфликтов, которые будут разрешаться при помощи механизма приоритетов.
Приоритет первого шаблона, паттерн которого соответствует продукции QName, будет равен 0
. Приоритет второго шаблона будет равен 0.5
, поскольку его паттерн не удовлетворяет другим условиям. Паттерн третьего шаблона имеет вид NCName:*
, а значит, его приоритет равен -0.25
. Приоритет четвертого шаблона равен -0.5
, поскольку его паттерн является проверкой узла (NodeTest
). Приоритет последнего, пятого шаблона будет равен 0, поскольку паттерн b соответствует продукции QName
.
Попробуем применить это преобразование к следующему документу:
<?ORA bypass="yes"?>
<b>
<a xmlns="a">
<b>
<b>
<c/>
</b>
</b>
</a>
</b>
Проследим за тем, как будет выполняться преобразование.
? Инструкции по обработке <?ORA bypass="yes"?>,
которая идет в документе первой, соответствует только один, четвертый шаблон.
? Корневому элементу b соответствуют два шаблона — четвертый и пятый, однако приоритет пятого шаблона выше, и поэтому применен будет именно он.
? Следующему элементу, a
, соответствуют третий и четвертый шаблоны. Здесь процессор должен применить третий шаблон, так как его приоритет выше, чем приоритет четвертого шаблона.
? Элемент b, включенный в элемент а
, соответствует первому, второму, третьему и четвертому шаблонам. Наивысший приоритет из них имеет второй шаблон.
? Следующему элементу b соответствуют первый, третий и четвертый шаблоны. В этом случае процессор выберет первый шаблон.
? Элемент с
соответствует третьему и четвертому шаблонному правилу. В этом случае процессор должен будет использовать третий шаблон.
Сравнивая этот анализ с сообщениями процессора, можно убедиться в верности прогноза:
4 template matched ORA.
5 template matched b.
3 template matched a.
2 template matched b.
1 template matched b.
3 template matched c.
Напомним, что приоритет преобразований может быть также явно указан в атрибуте priority
элемента xsl:template
. Например, если бы в предыдущем преобразовании четвертый шаблон был определен в виде
<xsl:template match="node()" priority="1">
<xsl:message>
<xsl:text>4</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
то его приоритет был бы выше, чем у всех остальных шаблонов, а поскольку он соответствует всем узлам в обрабатываемом документе, то во всех случаях применялся бы только он один. Сообщения процессора имели бы вид
4 template matched ORA.
4 template matched b.
4 template matched a.
4 template matched b.
4 template matched b.
4 template matched c.
Между тем, явное указание приоритета шаблона не может изменить порядок его импорта. То есть, если бы в предыдущем примере четвертый шаблон был бы вынесен во внешний модуль и импортирован в основное преобразование, в любом случае он бы применялся только в отсутствие более подходящих правил.
Пример
Листинг 5.16. Основное преобразование
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:a="a">
<xsl:import href="b.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:template match="a:b">
<xsl:message>
<xsl:text>1</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="a:a/a:b">
<xsl:message>
<xsl:text>2</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="a:*">
<xsl:message>
<xsl:text>3</xsl:text>
<xsl:call-template name="print-name"/>
</xsl :message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="b">
<xsl:message>
<xsl:text>5</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Листинг 5.17. Преобразование b.xsl
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:a="a">
<xsl:strip-space elements="*"/>
<xsl:template match="node()" priority="1">
<xsl:message>
<xsl:text>4</xsl:text>
<xsl:call-template name="print-name"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="print-name">
<xsl:text> template matched </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>.</xsl:text>
</xsl:template>
</xsl:stylesheet>
Как уже было сказано ранее, четвертое преобразование, вынесенное теперь в импортируемый модуль, при разрешении конфликтов шаблонов будет самым младшим вследствие того, что порядок его импорта меньше, чем у других преобразований. Сообщения процессора будут иметь вид
4 template matched ORA.
5 template matched b.
3 template matched a.
2 template matched b.
1 template matched b.
3 template matched c.
Кстати сказать, XSLT предоставляет возможность выполнять в преобразованиях импортированные шаблоны вместо тех, которые, по мнению процессора, лучше подходят. Подобно тому, как для применения шаблонных правил мы использовали элемент xsl:apply-templates
, импортированные шаблоны могут быть вызваны элементом xsl:apply-imports
.
- 4.14. Запрет и разрешение хостов
- Разрешение трассировки с помощью ‹trace›
- Устранение конфликтов имен WSDL с помощью свойства MessageName
- 4.1. Типы конфликтов налоговых юрисдикций
- Разрешение конфликтов имен
- Запрещение и разрешение прерываний
- Разрешение автоприращения для полей
- Как настроить разрешение экрана?
- 6.1. Разрешение экрана
- 12.3. Разрешение и блокировка конкретных программ
- Разрешение выполнения ActiveX-объектов на локальном компьютере
- Разрешение конфликтов шаблонов