XSLT 1.0: одновременное дублирование структур и подструктур

#xml #xslt #xslt-1.0

#xml #xslt #xslt-1.0

Вопрос:

Спасибо, что уделили мне время. Я постараюсь изложить это кратко и по существу.

Моя цель — использовать XSLT 1.0 для преобразования XML-файла так, чтобы он дублировал определенные узлы. Проблема в том, что мне нужно дублировать подструктуры структур, которые также необходимо дублировать. Я надеюсь, что следующий пример прояснит мою проблему.

Здесь исходный XML:

 <?xml version="1.0" encoding="UTF-8"?>
<parent_node>
    <derived_from_a>
        <element1>test</element1>
        <element2>test</element2>
    </derived_from_a>
    <derived_from_b>
        <element1>test</element1>
        <element2>test</element2>
        <derived_from_c>
            <element3>test</element3>
            <element4>test</element4>
        </derived_from_c>
    </derived_from_b>
</parent_node>
 

РЕДАКТИРОВАТЬ: мне нужно, чтобы все <derived_from_a> узлы дублировались в новый узел с именем <a> . То же самое должно произойти с узлами <derived_from_b> и <derived_from_c> для новых узлов <b> и <c> .

Это желаемый результат:

 <?xml version="1.0" encoding="UTF-8"?>
<parent_node>
    <derived_from_a>
        <element1>test</element1>
        <element2>test</element2>
    </derived_from_a>
    <a>
        <element1>test</element1>
        <element2>test</element2>
    </a>
    <derived_from_b>
        <element1>test</element1>
        <element2>test</element2>
        <derived_from_c>
            <element3>test</element3>
            <element4>test</element4>
        </derived_from_c>
        <c>
            <element3>test</element3>
            <element4>test</element4>
        </c>
    </derived_from_b>
    <b>
        <element1>test</element1>
        <element2>test</element2>
        <derived_from_c>
            <element3>test</element3>
            <element4>test</element4>
        </derived_from_c>
        <c>
            <element3>test</element3>
            <element4>test</element4>
        </c>
    </b>
</parent_node>
 

Обратите внимание, что я дублирую узел <derived_from_c> <c> в обе родительские структуры ( <derived_from_b> и <b> ). Это то, что мне нужно.

Я написал следующий XSLT:

 <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- -->
    <xsl:template match="//derived_from_a">
        <!-- first copy structure with original name -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
        <!-- then duplicate it -->
        <a>
            <xsl:copy-of select="child::node()"/>
        </a>
    </xsl:template>
    <xsl:template match="//derived_from_b">
        <!-- first copy structure with original name -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
        <!-- then duplicate it -->
        <b>
            <xsl:copy-of select="child::node()"/>
        </b>
    </xsl:template>
    <xsl:template match="//derived_from_c">
        <!-- first copy structure with original name -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
        <!-- then duplicate it -->
        <c>
            <xsl:copy-of select="child::node()"/>
        </c>
    </xsl:template>
</xsl:stylesheet>
 

Это генерирует этот вывод:

 <?xml version="1.0" encoding="UTF-8"?>
<parent_node>
    <derived_from_a>
        <element1>test</element1>
        <element2>test</element2>
    </derived_from_a>
    <a>
        <element1>test</element1>
        <element2>test</element2>
    </a>
    <derived_from_b>
        <element1>test</element1>
        <element2>test</element2>
        <derived_from_c>
            <element3>test</element3>
            <element4>test</element4>
        </derived_from_c>
        <c>
            <element3>test</element3>
            <element4>test</element4>
        </c>
    </derived_from_b>
    <b>
        <element1>test</element1>
        <element2>test</element2>
        <derived_from_c>
            <element3>test</element3>
            <element4>test</element4>
        </derived_from_c>
    </b>
</parent_node>
 

Обратите внимание, что узел <c> существует только в узле <derived_from_b> . Не в узле <b> .
Я безуспешно пытался переместить <derived_from_c> преобразование в верхнюю часть моего XSLT.

Есть ли способ дублировать все в одном сопоставлении?

Спасибо! Приветствую, Ник

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

1. <xsl:template match="//derived_from_a"> — обратите внимание, что нет необходимости привязывать выражения соответствия. Лучше написать это как <xsl:template match="derived_from_a"> есть.

Ответ №1:

Это преобразование XSLT 1.0 дает искомый результат:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:strip-space elements="*" />
  <xsl:output method="xml" indent="yes" />

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

  <xsl:template match="*[starts-with(name(), 'derived_from_')]">
    <xsl:call-template name="identity" />
    <xsl:element name="{substring-after(name(), 'derived_from_')}">
      <xsl:apply-templates select="node() | @*" />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
 

а именно

 <parent_node>
  <derived_from_a>
    <element1>test</element1>
    <element2>test</element2>
  </derived_from_a>
  <a>
    <element1>test</element1>
    <element2>test</element2>
  </a>
  <derived_from_b>
    <element1>test</element1>
    <element2>test</element2>
    <derived_from_c>
      <element3>test</element3>
      <element4>test</element4>
    </derived_from_c>
  </derived_from_b>
  <b>
    <element1>test</element1>
    <element2>test</element2>
    <derived_from_c>
      <element3>test</element3>
      <element4>test</element4>
    </derived_from_c>
    <c>
      <element3>test</element3>
      <element4>test</element4>
    </c>
  </b>
</parent_node>
 

Хитрость здесь в том, чтобы присвоить шаблону идентификатора как a match , так и a name . Таким образом, он может быть вызван как «в стиле извлечения» процессором XSLT при прохождении вашего входного документа, так и вручную <xsl:call-template> .

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


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

Чтобы избежать этого дублирования, вы можете встроить сопоставление в свой XSLT в пользовательском элементе и использовать самоссылку via document('') для динамического извлечения нового имени:

 <xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://tempuri.org"
>
  <my:map>
    <name from="derived_from_a" to="x" />
    <name from="derived_from_b" to="y" />
    <name from="derived_from_c" to="z" />
  </my:map>

  <!-- ... -->
   
  <xsl:template match="derived_from_a|derived_from_b|derived_from_c">
    <!-- ... -->
    <xsl:element name="{document('')/*/my:map/name[@from = name(current())]/@to}">
      <!-- ... -->
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
 

Добавление конфигурации с пользовательскими элементами иногда может быть весьма полезным. Другим вариантом может быть передача сопоставления через <xsl:param> вызывающий код из вызывающего кода, в зависимости от возможностей используемого вами инструмента XSLT. Третьим вариантом было бы использовать an <xsl:variable> , содержащий an <xsl:choose> , для определения нового имени элемента.

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

1. @nikiforos6 Рад слышать! Также смотрите Предложение, которое я только что добавил.