В каком порядке выполняются шаблоны в документе XSLT и соответствуют ли они исходному XML или буферизованному выводу? - PullRequest
66 голосов
/ 07 октября 2009

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

  1. В каком порядке выполняются шаблоны, а
  2. Когда они выполняются, совпадают ли они с (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. Я предполагаю, что Шаблон # 1 будет выполнен первым. Я не знаю, почему я так предполагаю - это только потому, что оно появляется первым в документе?
  2. Будет ли выполнен шаблон №2? Он соответствует узлу в исходном XML, но к тому времени, когда мы доберемся до этого шаблона (при условии, что он выполняется вторым), узел «firstName» не будет в выходном дереве.

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

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

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

На том же 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 создает новый узел с именем "fullName". Шаблон № 2 соответствует этому же узлу. Будет ли выполнен шаблон № 2, потому что узел «fullName» будет присутствовать в выходных данных к тому времени, когда мы перейдем к шаблону № 2?

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

Ответы [ 4 ]

83 голосов
/ 09 октября 2009

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

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

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

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

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

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

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

Будет ли выполнен шаблон №2? Это соответствует узлу в исходном XML, но к тому времени мы доберемся до этого шаблон (при условии, что он работает вторым), узел "firstName" не будет в дерево вывода.

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

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

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

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

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

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

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

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

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

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

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

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

6 голосов
/ 07 октября 2009

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

2 голосов
/ 07 октября 2009

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

В этом примере шаблон 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, а не для выходного.

0 голосов
/ 09 марта 2013

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

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

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

<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> 

[Прокрутите код выше вверх-вниз, если вы не видите все это]

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...