Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: DeaneОтветов: 4

Вот что меня всегда озадачивало в 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, отсюда и мой вопрос.

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

xslt

Ответы

Ответ 1 · рейтинг 93 · Evan Lenz

Мне нравится твой вопрос. Вы очень четко формулируете то, чего еще не понимаете. Вам просто нужно что-то связать. Я рекомендую вам прочитать "Как работает 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>). И эта древовидная структура изоморфна вашему фактическому результату. Не все осознают это или думали об этом таким образом; это потому, что у нас нет эффективных инструментов визуализации ... пока.

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

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

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

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

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

Ответ 2 · рейтинг 6 · mirod

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

Ответ 3 · рейтинг 2 · Chris R

В вашем 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, а не для вывода.

Ответ 4 · рейтинг 1 · user2151421

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

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

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

<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 - называемых записях.

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы