XSLT 1.0:同时复制结构和子结构

XSLT 1.0: Duplicate structures and substructures simultaneously

感谢您的宝贵时间。我会尽量保持简短和切题。

我的目标是使用 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> 的新节点中。对于新节点 <b><c>.

,节点 <derived_from_b><derived_from_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> 复制到两个父结构(<derived_from_b><b>)中的 <c> 中。这就是我需要的。

我编写了以下 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 的顶部,但没有成功。

有没有办法在一个映射中复制所有内容?

谢谢! 问候, 尼克

这个 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>

这里的技巧是为身份模板提供 matchname。这样,它既可以在 XSLT 处理器遍历您的输入文档时以“拉式”方式调用,也可以通过 <xsl:call-template>.

手动调用

手动调用使我们能够处理当前节点的后代,而不仅仅是复制它们,这满足了您的要求“复制也需要复制的结构的子结构。”


如果您的输出元素名称 (<a>) 实际上不是输入元素名称 (<derived_from_a>) 的子字符串,您可能会想复制工作模板。

为了避免重复,您可以将映射嵌入到自定义元素中的 XSLT 中,并通过 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 工具的功能。第三个选项是使用包含 <xsl:choose><xsl:variable> 来确定新元素名称。