Общий способ использования XSLT для создания текста из одного шаблона XML и другого аргумента XML

У меня есть решение моего вопроса ниже, но мне нужно, чтобы оно было более общим, поэтому мне нужна помощь.

Вопрос:

У нас есть "template.xml", который состоит из смеси элементов <verbatim> и элементов <argument id='foo'/>, каждый из которых может быть вложен или не вложен в элементы <part id='part001'>. (Поэтому дословные обозначения и аргументы могут быть в корневом элементе или в <part> ... </part> элементах).

Пример "template.xml":

<template_root>
  <verbatim>Title</verbatim>
  <part id='part001'>
    <verbatim>Nested in part 1.</verbatim>
    <argument id='keywords'>ARGKEY1, ARGKEY2, ARGKEY3</argument>
    <verbatim>End of part 1.</verbatim>
  </part>
  <part id='DoNotUse'>
    <verbatim>This should not be in the output</verbatim>
  </part>
  <verbatim>End of article</verbatim>
</template_root>

У нас есть еще один файл, "instance.xml", который состоит из элементов "‹ id='someid' />" (относится к ‹ аргумента /> в template.xml), которые опять же могут быть вложены или не вложены в те же элементы "‹ part id='partid' >", что и выше.

Пример "экземпляр.xml":

<instance_root template='template.xml'>
  <part id='part001'>
    <argument id="keywords" enabled='true'/>
  </part>
</instance_root>

Нам нужен XSLT, который считывает следующую информацию из «instance.xml» и генерирует

  1. Прочитайте имя файла шаблона (например, "template.xml"), а затем
  2. For all elements in the "template.xml":
    1. Copy value of all <verbatim> in the <template_root> of template.xml as is.
    2. Скопируйте значение всех <argument> в <template_root> файла template.xml, только если оно также указано в <instance_root> файла "instance.xml" с тем же @id и атрибутом @enabled='true'.
    3. Сделайте то же самое, что и (21) и (22) выше, для всех дочерних элементов каждого <part>, найденного только в «template.xml», и только если <part> с тем же @id также находится в «instance.xml», игнорируя все остальные <part> в « шаблон.xml".

Итак, что мы НЕ копируем: 1. Все <argument> в "template.xml", которым нет соответствующего <argument> в "instance.xml". 2. Все <part> в "template.xml", которым нет соответствующего <part> в "instance.xml".

Пример вывода текста:

Title
Nested in part 1.
ARGKEY1, ARGKEY2, ARGKEY3
End of part 1.
End of article

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


person RMZ    schedule 05.10.2016    source источник
comment
Ваши правила не совсем понятны. Объясните, точно какие элементы не следует копировать. Вы говорите, копируйте verbatim, но не делайте потомков part, которые не соответствуют какому-то условию, но verbatim также является потомком части. Если также непонятно, какое значение здесь играет внутренняя иерархия instance.xml, если она вообще есть. И почему у вас выводится текст, если вы должны копировать элементы.   -  person michael.hor257k    schedule 05.10.2016
comment
Пожалуйста, удалите эти комментарии и вместо этого отредактируйте свой вопрос.   -  person michael.hor257k    schedule 05.10.2016
comment
Готово. Это более ясно?   -  person RMZ    schedule 05.10.2016
comment
Это также может облегчить работу, если мы переименуем <template_root> и <instance_root> просто в <part> и разрешим вложение <part> элементов. Это также сработало бы для меня.   -  person RMZ    schedule 05.10.2016


Ответы (3)


Я надеюсь, что это решение уже достаточно общее. Взгляните на следующие строки:

расширенный шаблон.xml:

<template_root>
    <verbatim>Title</verbatim>
    <argument id="key">ARGKEY0</argument>
    <part id="part001">
        <verbatim>Nested in part 1.</verbatim>
        <argument id="keywords">ARGKEY1, ARGKEY2, ARGKEY3</argument>
        <verbatim>End of part 1.</verbatim>
    </part>
    <part id="part002">
        <verbatim>Nested in part 2.</verbatim>
        <argument id="keywords">ARGKEY4, ARGKEY5, ARGKEY6</argument>
        <verbatim>End of part 2.</verbatim>
    </part>
    <part id="part003">
        <verbatim>Nested in part 3.</verbatim>
        <argument id="keywords">ARGKEY7, ARGKEY8, ARGKEY9</argument>
        <verbatim>End of part 3.</verbatim>
    </part>
    <part id="DoNotUse">
        <verbatim>This should not be in the output</verbatim>
    </part>
    <argument id="word">ARGKEY10</argument>
    <verbatim>End of article</verbatim>
</template_root>

расширенный instance.xml:

<instance_root template='template.xml'>
    <part id='part001'>
        <argument id="keywords" enabled='true'/>
    </part>
    <part id="part002">
        <argument id="keywords" enabled="false"/>
    </part>
    <part id="part003">
        <argument id="keywords" enabled="true"/>
    </part>
    <argument id="key" enabled='false'/>
    <argument id="word" enabled='true'/>
</instance_root>

Таблица стилей:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="instance" select="document('instance.xml')/*"/>

    <xsl:template match="template_root">
        <xsl:copy>
            <xsl:apply-templates select="
                verbatim
                | argument[@id = $instance/argument[@enabled = 'true']/@id]
                | part[@id = $instance/part/@id]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="part">
        <xsl:copy>
            <xsl:apply-templates select="
                verbatim
                | argument[@id = $instance/part[@id = current()/@id]/argument[@enabled = 'true']/@id]
                "/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

[Подсказка: нельзя работать с совпадениями шаблонов, потому что невозможно использовать переменные в совпадениях шаблонов.]

Результат в формате XML:

<?xml version="1.0" encoding="UTF-8"?>
<template_root>
  <verbatim>Title</verbatim>
  <part>
    <verbatim>Nested in part 1.</verbatim>
    <argument id="keywords">ARGKEY1, ARGKEY2, ARGKEY3</argument>
    <verbatim>End of part 1.</verbatim>
  </part>
  <part>
    <verbatim>Nested in part 2.</verbatim>
    <verbatim>End of part 2.</verbatim>
  </part>
  <part>
    <verbatim>Nested in part 3.</verbatim>
    <argument id="keywords">ARGKEY7, ARGKEY8, ARGKEY9</argument>
    <verbatim>End of part 3.</verbatim>
  </part>
  <argument id="word">ARGKEY10</argument>
  <verbatim>End of article</verbatim>
</template_root>

Результат в виде текста

Title
Nested in part 1.
ARGKEY1, ARGKEY2, ARGKEY3
End of part 1.
Nested in part 2.
End of part 2.
Nested in part 3.
ARGKEY7, ARGKEY8, ARGKEY9
End of part 3.
ARGKEY10
End of article

Вы должны изменить некоторые мелочи, чтобы добиться точного результата! Изменить/добавить следующее:

<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="text()" priority="1.1">
    <xsl:value-of select="concat(., '&#10;')"/>
</xsl:template>

Самый важный и, возможно, недостаток: это не будет работать с //part/part! [Невозможно проверить ancestor::part, если все соответствует правильному @id]

person uL1    schedule 05.10.2016
comment
Привет ЮЛ1. Это прекрасно выглядит и многое проясняет. Спасибо что нашли время ответить. Единственная проблема, которая у меня есть, заключается в том, что instance.xml должен ссылаться на то, какой шаблон использовать, а не наоборот. template.xml не должен не ссылаться на какой-либо файл экземпляра. Не могли бы вы также немного пояснить свои намеки? Что вы имеете в виду под «Не могу работать с совпадениями шаблонов» и зачем, по вашему мнению, они нам нужны? Кроме того, что вы подразумеваете под «Невозможно проверить ancestor::part, если все соответствует правильному @id»? - person RMZ; 06.10.2016
comment
Есть и другие способы описать вопрос. Я могу попробовать более простой способ объяснить вопрос: у нас есть instance.xml, он говорит нам, какой шаблон выбрать. Мы должны прочитать instance.xml и скопировать значения элементов, которые этот экземпляр включает, из шаблона, на который он ссылается, в том порядке, в котором они появляются. Эти элементы могут быть вложенными, но пути к элементам в экземпляре соответствуют тем же путям в шаблоне. - person RMZ; 06.10.2016

Итак, благодаря ответу @uL1 и небольшой настройке, ниже приведено очень хорошее решение, которое правильно выполняет свою работу.

Единственная недостающая функция (которую я действительно не задавал в своем первоначальном вопросе, но мне бы очень хотелось добавить) - это возможность обрабатывать любое количество вложенных элементов <part>.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" indent="yes"/>
  <xsl:variable name="template" select="document(//@template)"/>
  <xsl:variable name="instance" select="/"/>

  <xsl:template match="text()" priority="1.1">
    <xsl:value-of select="concat(., '&#10;')"/>
  </xsl:template>

  <xsl:template match="/"> <!-- Using match="instance_root" would work just as well. -->
    <xsl:apply-templates select="$template/*"/>
  </xsl:template>

  <xsl:template match="template_root">
    <xsl:copy>
        <xsl:apply-templates select="
            verbatim
            | argument[@id = $instance//argument[@enabled = 'true']/@id]
            | part[@id = $instance//part/@id]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="part">
    <xsl:copy>
        <xsl:apply-templates select="
            verbatim
            | argument[@id = $instance//part[@id = current()/@id]/argument[@enabled = 'true']/@id]"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
person RMZ    schedule 06.10.2016

Заполнить пробелы — это хорошо известный шаблон дизайна XSLT.

См. этот ответ для подробного объяснения и фактического кода.

person Dimitre Novatchev    schedule 07.10.2016