Шаблоны XSLT для создания недостающих элементов, конфликтующих друг с другом

#xml #xslt

#xml #xslt

Вопрос:

Я использую преобразование XSLT для добавления элементов configuration и status к моим XML-данным в случае, если один или оба из них отсутствуют. Я хотел бы использовать независимые шаблоны для обработки каждого из них, но, похоже, вступает в силу только один из шаблонов.

Исходные данные:

 <data>
  <environment>
    <id>test</id>
    <details>Detail info for environment...</details>
  </environment>
  <default_conf>abcd1234</default_conf>
  <default_status>1</default_status>
</data>
 

XSLT:

 <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />
    <!-- identity transformation -->
    <xsl:template match="/ | @* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()" />
        </xsl:copy>
    </xsl:template>
 <!-- if configuration not given, create it with the value of default_conf -->
 <xsl:template match="data[not(configuration)]">
   <xsl:copy>
     <xsl:apply-templates/>
        <!--xsl:apply-templates select="@*|node()"/-->
       <configuration><xsl:value-of select="default_conf"/></configuration>
   </xsl:copy>
 </xsl:template>
 <!-- if status not given, create it with the value of default_status -->
 <xsl:template match="data[not(status)]">
   <xsl:copy>
     <xsl:apply-templates/>
     <!--xsl:apply-templates select="@*|node()"/-->
     <status><xsl:value-of select="default_status"/></status>
   </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
 

В результате XML создается только элемент, но не элемент. Что не так с шаблонами преобразования и как это исправить?

Пожалуйста, обратите внимание, что мне нужно также передать default_conf default status элементы и, поэтому я не собираюсь переименовывать эти элементы.

Желаемый результат:

 <data>
  <environment>
    <id>test</id>
    <details>Detail info for environment...</details>
  </environment>
  <default_conf>abcd1234</default_conf>
  <default_status>1</default_status>
  <configuration>abcd1234</configuration>
  <status>1</status>
</data>
 

Комментарии:

1. С этим трудно справиться с двумя разными шаблонами, соответствующими одному и тому же элементу, если вы не выполните два этапа преобразования, например. сохраните результат добавления первого элемента в переменную, а затем передайте его через другую обработку. Это довольно просто в XSLT 2 или более поздней версии, но в XSLT 1 любая переменная будет фрагментом результирующего дерева, и вам нужно использовать exsl:node-set или аналогичный, чтобы преобразовать его в набор узлов. В качестве альтернативы вы могли бы рассмотреть возможность добавления проверок с помощью, например <xsl:if test="not(status)"><status>...</status></xsl:if> , и аналогично для другого элемента в одном шаблоне.

Ответ №1:

В XSLT,

Узел обрабатывается путем нахождения всех правил шаблона с шаблонами, которые соответствуют узлу, и выбора наилучшего из них;
https://www.w3.org/TR/1999/REC-xslt-19991116/#section-Processing-Model

Если оба configuration и status отсутствуют, у вас есть два шаблона, которые соответствуют одному и тому же узлу с одинаковым приоритетом. Это ошибка:

Процессор XSLT может сигнализировать об ошибке; если он не сигнализирует об ошибке, он должен восстановить, выбрав из оставшихся правил шаблона соответствия то, которое встречается последним в таблице стилей.
https://www.w3.org/TR/1999/REC-xslt-19991116/#conflict

Простое решение — использовать один шаблон с двумя xsl:if инструкциями для добавления каждого отсутствующего узла. В противном случае вам нужно будет использовать не два, а три шаблона — и убедитесь, что добавленный шаблон имеет приоритет.

Комментарии:

1. Спасибо michael.hor257k, который помог мне разработать решение в виде единого шаблона с инструкцией if для каждого необязательного элемента.