Книга: Технология 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.

Оглавление книги


Генерация: 1.787. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз