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

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

  1. В каком порядке выполняются шаблоны и
  2. Когда они выполняются, соответствуют ли они (а) исходному исходному XML или (б) текущему выходу 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, отсюда и мой вопрос.


person Deane    schedule 07.10.2009    source источник
comment
Вы пропустили / в теге второго лица в вашем примере xml.   -  person Chris R    schedule 07.10.2009
comment
Дзен, который я усвоил, - процесс выполнения XSLT ориентирован на XML, а не на XSL. Структура XML управляет потоком, а не структура XSL. Это была одна из больших частей, которую я не понимал все эти годы.   -  person Deane    schedule 07.10.2009
comment
match = // fullName совпадает с match = fullName. шаблон проверяет, соответствует ли данный узел ему из любого контекста, в отличие от выражения XPath expression, которое выбирает узлы из определенного < / i> контекст.   -  person Evan Lenz    schedule 09.10.2009


Ответы (4)


Мне нравится твой вопрос. Вы очень четко формулируете то, чего еще не понимаете. Вам просто нужно что-то связать. Я рекомендую вам прочитать «Как работает 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 создает новый узел с именем «fullName». Шаблон № 2 соответствует тому же узлу. Будет ли шаблон № 2 выполняться, потому что узел «fullName» существует в выходных данных к тому времени, когда мы дойдем до шаблона № 2?

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

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

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

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

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

person Evan Lenz    schedule 09.10.2009
comment
Кстати, то, что вы пытались сделать (написать правила шаблона для узлов обработки, созданных другими правилами шаблона), на самом деле круто, хотя, как я уже сказал, это не сработает, если вы явно не настроите конвейер с использованием переменных и ссылок на переменные. Я иногда задавался вопросом о создании контекста обработки, в котором процессор XSLT будет многократно вызываться для результата, пока все элементы (например, в конкретном пространстве имен макроса) не будут обработаны и больше не появятся в результате - вроде как рекурсивный механизм включения. Это было бы удобно. Конвейеры в XSLT и так синтаксически неуклюжи. - person Evan Lenz; 09.10.2009
comment
Эван, ваш ответ и образец главы были действительно фантастическими. Думаю, я это понимаю, за исключением одного вопроса, который я опубликую отдельно. - person Deane; 10.10.2009
comment
Превосходная статья от @EvanLenz !! Большое спасибо. - person GuruM; 28.02.2012
comment
Отличный ответ, действительно помог мне понять. Я согласен с тем, что инструменты визуализации будут огромным подспорьем, особенно если они смогут точно увидеть, какие операторы выбирают контент в источнике, какие генерируют контент в выходных данных и как управляются текущий узел и контекст. - person chrispitude; 23.12.2019
comment
В последние годы я добился некоторого прогресса в создании инструмента визуализации: github.com/evanlenz/xslt-visualizer < / а> - person Evan Lenz; 12.05.2020

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

person mirod    schedule 07.10.2009
comment
Насколько я понимаю, если 2 или более шаблонов соответствуют одному и тому же узлу, то будет работать тот, у которого есть наиболее точное соответствие. Я полагаю, вы имеете в виду 2 или более с одинаковыми условиями совпадения? - person Chris R; 07.10.2009
comment
sorta, правила приведены в спецификации: w3.org/TR/xslt#conflict. Если вы не используете атрибут приоритета, то многие шаблоны имеют одинаковый приоритет. Обратите внимание на последний абзац раздела спецификации: процессор XSLT может сигнализировать об ошибке. - person mirod; 07.10.2009
comment
Я не знал правила об одном и том же приоритете: либо с ошибкой, либо с использованием последнего, в зависимости от реализации процессора, либо о том, что вы можете установить приоритет самостоятельно с помощью атрибута, спасибо. - person Chris R; 07.10.2009

В вашем 1-м примере шаблон № 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-м примере шаблон № 2 не будет выполняться, потому что шаблон запускается для входного xml, а не для вывода.

person Chris R    schedule 07.10.2009

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

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

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

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

[Scroll the code above up-down if you can't see it all]

Это работает так: основной шаблон всегда совпадает - совпадает на /

Здесь есть фрагменты кода - шаблоны, которые вызываются.

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

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

person user2151421    schedule 09.03.2013