В каком порядке выполняются шаблоны в документе XSLT и соответствуют ли они исходному XML или буферизованному результату?

Вот что всегда озадачило меня в XSLT:

  • В каком порядке выполняются шаблоны и
  • Когда они выполняются, соответствуют ли они (a) исходному XML-источнику или (b) текущий вывод XSLT в эту точку?

Пример:

<person>
  <firstName>Deane</firstName>
  <lastName>Barker</lastName>
</person>

Вот фрагмент XSLT:

<!-- Template #1 -->
<xsl:template match="/">
  <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="/person/firstName">
  First Name: <xsl:value-of select="firstName"/>
</xsl:template>

Два вопроса:

  • Я предполагаю, что шаблон № 1 будет выполнен первым. Я не знаю, почему я предполагаю это - это только потому, что он появляется первым в документе?
  • Выполняется ли шаблон №2? Он соответствует node в исходном XML, но к тому моменту, когда мы получим этот шаблон (при условии, что он выполняется второй), "firstName" node не будет в дереве вывода.

Итак, являются ли "последующие" шаблоны обязательными для того, что произошло в "ранних" шаблонах, или они работают с исходным документом, не обращая внимания на то, что было им "доведено"? (Все эти слова в кавычках, потому что мне трудно обсуждать вопросы, основанные на времени, когда я действительно мало понимаю, как определяется порядок шаблонов в первую очередь...)

В приведенном выше примере у нас есть шаблон, который соответствует корню node ( "/" ), который - когда он выполняется, - существенно удалил все узлы из вывода. В этом случае это предотвратит выполнение всех других шаблонов, поскольку после завершения этого первого шаблона не будет ничего подходящего?

К этому моменту я был обеспокоен тем, что более поздние шаблоны не выполняются, потому что узлы, на которых они работали, не отображаются в выходном файле, но как насчет обратного? Может ли "более ранний" шаблон создать node, что "более поздний" шаблон может что-то сделать с?

В том же XML, что и выше, рассмотрим этот XSL:

<!-- Template #1 -->
<xsl:template match="/">
  <fullName>
    <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
  </fullName>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="//fullName">
  Full Name: <xsl:value-of select="."/>
</xsl:template>

Шаблон №1 создает новый node, называемый "fullName". Шаблон № 2 совпадает с тем же node. Будет ли Шаблон №2 выполняться, потому что "fullName" node существует в выходе к моменту, когда мы обходим шаблон Template # 2?

Я понимаю, что я глубоко не осведомлен о "дзэн" XSLT. На сегодняшний день мои таблицы стилей состоят из шаблона, соответствующего корню node, а затем являются полностью процедурными. Я устал от этого. Я бы предпочел правильно понять XSLT правильно, поэтому мой вопрос.

Ответ 1

Мне нравится ваш вопрос. Вы очень четко говорите о том, чего еще не поняли. Вам просто нужно что-то связать. Моя рекомендация заключается в том, что вы читаете "Как работает XSLT" , в главе, которую я написал, чтобы точно адресовать вопросы, которые вы задаете. Я хотел бы услышать, если это связывает вещи для вас.

Менее формально я возьмусь за ответ на каждый из ваших вопросов.

  • В каком порядке выполняются шаблоны и
  • Когда они выполняются, соответствуют ли они (a) исходному XML-источнику или (b) текущий выход XSLT для этого точка?

В любой заданной точке обработки XSLT в определенном смысле есть два контекста, которые вы идентифицируете как (a) и (b): где вы находитесь в исходном дереве и где находитесь в дереве результатов. Где вы находитесь в исходном дереве, называется текущий node. Он может меняться и перемещаться по всему дереву источника, поскольку вы выбираете произвольные наборы узлов для обработки с использованием XPath. Однако, концептуально, вы никогда не "прыгаете" по дереву результатов таким же образом. XSLT-процессор строит его упорядоченным образом; сначала он создает корень node дерева результатов; затем добавляет детей, создавая результат в порядке документа (сначала в глубину). [Ваше сообщение мотивирует меня снова забрать мою визуализацию программного обеспечения для экспериментов XSLT...]

Порядок правил шаблона в таблице стилей никогда не имеет значения. Вы не можете сказать, просто взглянув на таблицу стилей, в каком порядке будут создаваться правила шаблона, сколько раз будет создаваться правило, или даже будет ли оно вообще. (match="/" является исключением, вы всегда можете знать, что он будет запущен.)

Я предполагаю, что шаблон № 1 будет выполнить сначала. Я не знаю, почему я предположим, что это - только потому, что сначала появляется в документе?

Неа. Его можно было бы назвать первым, даже если вы поместили его последним в документе. Порядок правил шаблона никогда не имеет значения (за исключением условия ошибки, когда у вас более одного правила шаблона с тем же приоритетом, что и тот же самый node, даже тогда он необязателен для разработчика, и вы никогда не должны полагаться на такое поведение). Он сначала вызван, потому что первое, что всегда происходит при запуске XSLT-процессора, - это виртуальный вызов <xsl:apply-templates select="/"/> . Один виртуальный вызов создает все дерево результатов. Ничего не происходит за ее пределами. Вы можете настроить или "настроить" поведение этой команды, задав правила шаблона.

Выполняется ли шаблон №2? Он соответствует node в исходном XML, но к тому времени, когда мы доберемся до этого шаблон (если он выполняется второй), "firstName" node не будет находиться в дерево вывода.

Шаблон №2 (или любые другие правила шаблонов) никогда не будет запущен, если у вас нет вызова <xsl:apply-templates/> где-то в правиле match="/". Если у вас их нет, тогда никакие правила шаблонов, кроме match="/", не будут запущены. Подумайте об этом так: для того, чтобы правило шаблона активировалось, оно не может просто соответствовать значению node на входе. Он должен соответствовать node, который вы выбираете для обработки (используя <xsl:apply-templates/>). И наоборот, он будет продолжать соответствовать node столько раз, сколько вы захотите обработать его.

Будет ли [ match="/"template] pre-empt все остальные шаблоны от выполнения, поскольку нет ничего после первого шаблона завершено?

Это правило вытесняет остальные, нигде не включая <xsl:apply-templates/>. Есть еще множество узлов, которые могут быть обработаны в исходном дереве. Они всегда там, созрели для выбора; обрабатывайте каждый из них столько раз, сколько хотите. Но единственный способ обработать их с помощью правил шаблона - это вызвать <xsl:apply-templates/>.

К этому моменту я был обеспокоен с последующими шаблонами, не выполняющими потому что используемые им узлы on не отображаются на выходе, но как насчет обратного? Может "ранее" создайте node, который шаблон "позже" может сделать что-то с?

Не так, что шаблон "раньше" создает новый node для обработки; что "ранее" шаблон в свою очередь обрабатывает больше узлов из исходного дерева, используя ту же самую инструкцию (<xsl:apply-templates). Вы можете думать об этом как о том, что вызываете одну и ту же "функцию" рекурсивно, с разными параметрами каждый раз (узлы обрабатываются в соответствии с контекстом и атрибутом select).

В конце концов, вы получаете древовидный стек рекурсивных вызовов к одной и той же "функции" (<xsl:apply-templates>). И эта древовидная структура изоморфна к вашему фактическому результату. Не все это понимают или думали об этом таким образом; потому что у нас нет эффективных инструментов визуализации... пока.

Шаблон №1 создает новый node, называемый "полное имя". Шаблон №2 соответствует тот же node. Будет ли шаблон №2 выполнить, потому что "fullName" nodeсуществует на выходе к тому времени, когда мы обойти шаблон №2?

Неа. Единственный способ сделать цепочку обработки - это явно настроить ее таким образом. Создайте переменную, например $tempTree, которая содержит новый элемент <fullName>, а затем обработайте его, как этот <xsl:apply-templates select="$tempTree">. Для этого в XSLT 1.0 вам нужно обернуть ссылку на переменную с помощью функции расширения (например, exsl:node-set()), но в XSLT 2.0 она будет работать так же, как и.

Выполняете ли вы обработку узлов из исходного исходного дерева или во временное дерево, которое вы создаете, в любом случае вам нужно явно указать, какие узлы вы хотите обработать.

То, что мы не рассмотрели, - это то, как XSLT получает все свое неявное поведение. Вы также должны понимать встроенные правила шаблонов. Я постоянно пишу таблицы стилей, которые даже не содержат явного правила для корня node (match="/"). Вместо этого я полагаюсь на встроенное правило для корневых узлов (применять шаблоны к дочерним элементам), что совпадает с встроенным правилом для узлов элемента. Таким образом, я могу игнорировать большие части ввода, пусть XSLT-процессор автоматически пересекает его, и только когда он встретится с node, меня интересует, сделаю ли я что-то особенное. Или я мог бы написать одно правило, которое копирует все рекурсивно (называемое преобразованием идентичности), переопределяя его только там, где это необходимо, для внесения дополнительных изменений во входные данные. После того, как вы прочитали "Как работает XSLT", ваше следующее назначение - посмотреть "преобразование идентичности".

Я понимаю, что я глубоко неосведомлен о "дзэн" XSLT. На сегодняшний день мой таблицы стилей состояли из шаблон, соответствующий корню node, затем являются полностью процедурными оттуда. Я устал от этого. я бы скорее на самом деле понимают XSLT правильно, следовательно, мой вопрос.

Я приветствую вас. Теперь пришло время взять "красную таблетку": прочитайте "Как работает XSLT"

Ответ 2

Шаблоны всегда соответствуют исходному XML. Таким образом, порядок не имеет значения, если только два или более шаблона не совпадают с тем же node (s). В этом случае, несколько контр-интуитивно, запускается правило с шаблоном соответствия last.

Ответ 3

В первом примере Шаблон №1 запускается, потому что, когда вы начинаете обработку входного xml, он начинается с корня, и это единственный шаблон в вашей таблице стилей, который соответствует корневому элементу. Даже если он был 2-м в таблице стилей, он все равно выполнил бы 1-й.

В этом примере шаблон 2 не будет запущен, так как вы уже обработали корневой элемент, используя шаблон 1, и больше нет элементов для обработки после корня. Если вы хотите обработать другие элементы с помощью дополнительных шаблонов, вы должны изменить их на.

<xsl:template match="/">
  <xsl:apply-templates/>
</xsl:template>

Затем это позволяет вам определить шаблон для каждого интересующего вас элемента и обработать xml более логичным способом, а не делать это процедурно.

Также обратите внимание, что в этом примере не будет выводиться ничего, как в текущем контексте (в корне) отсутствует элемент firstName, а только элемент person, поэтому он должен быть:

<xsl:template match="/">
  <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>

Мне будет легче подумать, что вы переходите через xml, начиная с корня и ища шаблон, соответствующий этому элементу, затем следуя этим инструкциям для генерации вывода. XSLT преобразует входной документ в выходной файл, поэтому выходная доза пуста в начале преобразования. Выход не используется как часть преобразования, это просто выход из него.

В вашем втором примере шаблон №2 не будет выполняться, потому что шаблон выполняется против ввода xml, а не для вывода.

Ответ 4

Ответ Эвана в основном хороший.

Однако одна вещь, которая, по-видимому, отсутствует, - это возможность "вызывать" куски кода без какого-либо соответствия. Это, по крайней мере, по мнению некоторых людей, позволит значительно улучшить структурирование.

Я сделал небольшой пример, пытаясь показать, что я имею в виду.

<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
    <!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>

    <body>
<!-- Comments are better than nothing -->
    <!-- but that part should really have been somewhere else ... -->

<!-- Now do what we really want here ... this really is making the table! -->

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->

<!-- This approach works, but leads to horribly monolithic code -->
    <!-- Further - it leads to templates including code which is strictly -->
    <!-- not relevant to them. I've not found a way round this yet -->
</xsl:template>

Однако, немного поиграв, и сначала используя подсказку, что, если есть два подходящих шаблона, последний будет выбран в коде, а затем реструктурирует мой код (не все показано здесь), я достиг это, похоже, работает, и, надеюсь, генерирует правильный код, а также отображает нужные данные -

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->

<xsl:template name="dohtml">
  <html>
      <xsl:call-template name="dohead" />
      <xsl:call-template name="dobody" />
  </html>
</xsl:template>

<xsl:template name="dohead">
<head>
    <title>Salary details</title>
</head>
</xsl:template>

<xsl:template name="dobody">
<body>
    <xsl:call-template name="dotable" />
</body>
</xsl:template>

<xsl:template match="/entries" name="dotable">

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

</xsl:template>

<xsl:template  match="/" name="main">
            <xsl:call-template name="dohtml" />
</xsl:template> 

[Прокрутите код вверху вниз, если вы не можете его увидеть]

Как это работает, основной шаблон всегда совпадает - совпадает с /

У этого есть куски кодовых шаблонов - которые вызываются.

Теперь это означает, что невозможно сопоставить другой шаблон в /, но можно сопоставить явно на названном node, который в этом случае является наивысшим уровнем node в записях xml.

Небольшая модификация кода привела пример, приведенный выше.