#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 Рад слышать! Также смотрите Предложение, которое я только что добавил.