如何将所有子节点原样复制到生成的树中?

时间:2012-03-25 17:41:29

标签: xslt

UPDATE(剧透):这个问题得到了解答(请参阅下面的David Carlisle回答),它看起来像是JRE中某些版本中包含的XSLT实现中的错误(就像它在jdk中工作但在jre 1.6.0_20上不起作用) -b02并且在1.6.0_31-b05完全没有工作。我在Oracle网站上记录了一个bug。

我几乎就在那里,在功能方面它现在有效。但是我对它的某些部分不满意,其中一些(我相信)可能会更短。这是一个问题......详见下文

这是输入xml

的示例
<?xml version="1.0" encoding="UTF-8"?>
<t1>
    <t2 a1="v1">
        <ot1 a2="v2" />
        <ot2 a3="v3">
            <t3 a5="v4">
                <ot1 a2="v5" />
            </t3>
        </ot2>
    </t2>
</t1>

这是预期结果xml的一个示例(请参阅下面的xslt文件,了解最新信息 -

<?xml version="1.0" encoding="UTF-8"?>
<t1>
    <t2 a1="v1">
        <gt2 a="ot2" b="gtv1">
            <gt1 a="ot1">v2</gt1>
        </gt2>
        <gt2 a="ot2" b="v3">
            <t3 a5="v4">
                <gt2 a="ot2" b="gtv1">
                    <gt1 a="ot1">v5</gt1>
                </gt2>
            </t3>
        </gt2>
    </t2>
</t1>

这是xslt我终于结束了(但不太高兴)。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common" version="1.0"
    exclude-result-prefixes="exslt">
    <xsl:output method="xml" />

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

    <xsl:template match="ot1|ot2">
        <xsl:variable name="thisResult">
            <xsl:apply-templates select="." mode="impl" />
        </xsl:variable>
        <xsl:apply-templates select="exslt:node-set($thisResult)" />
    </xsl:template>

    <xsl:template match="ot1" mode="impl">
        <ot2 a3="gtv1">
            <gt1 a="ot1">
                <xsl:value-of select="@a2" />
            </gt1>
        </ot2>
    </xsl:template>

    <xsl:template match="ot2" mode="impl">
        <gt2 a="ot2" b="{@a3}">
            <xsl:for-each select="child::*">
                <xsl:element name="{name()}">
                    <xsl:copy-of select="@*|node()" />
                </xsl:element>
            </xsl:for-each>
        </gt2>
    </xsl:template>
</xsl:stylesheet>

问题是:如何缩短它?

<xsl:for-each select="child::*">
    <xsl:element name="{name()}">
        <xsl:copy-of select="@*|node()" />
    </xsl:element>
</xsl:for-each>

我尝试了以下内容,但在这种情况下,生成的xml的一部分将丢失(特别是gt2元素的属性将丢失)

<xsl:copy-of select="node()"/>

也尝试了

<xsl:copy-of select="*"/>

没有成功

2 个答案:

答案 0 :(得分:3)

  

我需要定义能够执行所谓的xslt转换   “多次转型”。

此处无需使用多次传递处理

这个简短而简单的转型:

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

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

 <xsl:template match="ot1">
  <gt1 a="ot1"><xsl:value-of select="@a2"/></gt1>
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="ot2">
  <gt2 a="ot2" b="{@a3}"/>
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<t1>
    <t2 a1="v1">
        <ot1 a2="v2" />
        <ot2 a3="v3">
            <t3 a5="v4">
                <ot1 a2="v5" />
            </t3>
        </ot2>
    </t2>
</t1>

生成想要的正确结果

<t1>
   <t2 a1="v1">
      <gt1 a="ot1">v2</gt1>
      <gt2 a="ot2" b="v3"/>
      <t3 a5="v4">
         <gt1 a="ot1">v5</gt1>
      </t3>
   </t2>
</t1>

更新:在OP已经与自己认为需要多通道处理更新的问题 - 这仍然是不正确的。

以下是新问题的简短解决方案,一次性传递

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

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

 <xsl:template match="ot1">
  <gt2 a="ot2" b="gtv{substring-after(name(), 'ot')}">
    <gt1 a="ot1"><xsl:value-of select="@a2"/></gt1>
  </gt2>
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="ot2">
  <gt2 a="ot2" b="{@a3}"/>
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于新提供的XML源文档时:

<t1>
    <t2 a1="v1">
        <ot1 a2="v2" />
        <ot2 a3="v3">
            <t3 a5="v4">
                <ot1 a2="v5" />
            </t3>
        </ot2>
    </t2>
</t1>

再次生成想要的正确结果

<t1>
   <t2 a1="v1">
      <gt2 a="ot2" b="gtv1">
         <gt1 a="ot1">v2</gt1>
      </gt2>
      <gt2 a="ot2" b="v3"/>
      <t3 a5="v4">
         <gt2 a="ot2" b="gtv1">
            <gt1 a="ot1">v5</gt1>
         </gt2>
      </t3>
   </t2>
</t1>

<强> UPDATE2 :由于OP是要求的他的当前代码重构,特别需要更好地表达此摘录:

<xsl:for-each select="child::*">
  <xsl:element name="{name()}">
      <xsl:copy-of select="@*|node()" />
  </xsl:element>
</xsl:for-each>

这是一个明显的重构 - 只需用

替换上面的内容
<xsl:apply-templates/>

完成此修改后,完整代码

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:exslt="http://exslt.org/common" version="1.0"
        exclude-result-prefixes="exslt">
        <xsl:output omit-xml-declaration="yes" indent="yes" />
        <xsl:strip-space elements="*"/>

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

        <xsl:template match="ot1|ot2">
            <xsl:variable name="thisResult">
                <xsl:apply-templates select="." mode="impl" />
            </xsl:variable>
            <xsl:apply-templates select=
            "exslt:node-set($thisResult)" />
        </xsl:template>

        <xsl:template match="ot1" mode="impl">
            <ot2 a3="gtv1">
                <gt1 a="ot1">
                    <xsl:value-of select="@a2" />
                </gt1>
            </ot2>
        </xsl:template>

        <xsl:template match="ot2" mode="impl">
            <gt2 a="ot2" b="{@a3}">
                <xsl:apply-templates/>
            </gt2>
        </xsl:template>
</xsl:stylesheet>

并应用于最新提供的源XML文档

<t1>
    <t2 a1="v1">
        <ot1 a2="v2" />
        <ot2 a3="v3">
            <t3 a5="v4">
                <ot1 a2="v5" />
            </t3>
        </ot2>
    </t2>
</t1>

生成了想要的结果

<t1>
   <t2 a1="v1">
      <gt2 a="ot2" b="gtv1">
         <gt1 a="ot1">v2</gt1>
      </gt2>
      <gt2 a="ot2" b="v3">
         <t3 a5="v4">
            <gt2 a="ot2" b="gtv1">
               <gt1 a="ot1">v5</gt1>
            </gt2>
         </t3>
      </gt2>
   </t2>
</t1>

答案 1 :(得分:2)

Dimitre已经展示了另一种策略,但为了回答原始帖子中的问题,xsl:for-each(除了一些未显示在此处的命名空间效果)相当于一个

<xsl:copy-of select="*"/>

如果替换xsl:for-each,我会获得相同的输出。

您建议的替代

<xsl:copy-of select="node()"/>

几乎相同,但是拾取用于缩进源的空白节点,因此输出在空白区域不同。

你说

  

(特别是gt3元素的属性将丢失)

但编码更改不会更改属性,示例输入或输出中没有gt3元素?