XSLT - 复制孙子重新排序

时间:2013-03-27 12:40:52

标签: xslt

我对XSLT很陌生,我试图复制一个已有的XML文件,但重新排序的元素却在试图重新排序孙子时遇到困难。

假设我有这样的意见:

<grandParent>
    <parent> 
        <c>789</c>
        <b>
           <b2>123</b2>
           <b1>456</b1>
        </b>
        <a>123</a>
    </parent>
    ....
</grandParent>

我想要做的是获取相同的XML文件,但是将标签的顺序更改为a,b,c,其顺序为b = b1,b2。 所以我开始使用XSLT文件:

<xsl:template match="node()|@*">    <- This should copy everything as it is
    <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:copy-of select="a"/>
        <xsl:copy-of select="b"/>
        <xsl:copy-of select="c"/>
    </xsl:copy>
</xsl:template>

但是“xsl:copy-of select =”b“”复制指定的元素(b2,b1)。 我尝试使用另一个xsl:template“grandParent / parent / b”,但没有帮助。

也许我没有按照正确的方式做事......有什么提示吗?

谢谢!

解决方案 - 感谢Nils

你的解决方案可以很好地运行Nils,我只是对它进行了一些调整以适应我当前的情况,其中“b”是可选的,并且标签的名称可能不相关。 最终的代码是这样的:

<xsl:template match="node()|@*">    <- This should copy everything as it is
    <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
</xsl:template>
<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:copy-of select="a"/>
                    <xslt:if test="b">
                        <b>
                        <xsl:copy-of select="b1"/>
                        <xsl:copy-of select="b2"/>
                        </b>
                    </xslt:if>
        <xsl:copy-of select="b"/>
        <xsl:copy-of select="c"/>
    </xsl:copy>
</xsl:template>

3 个答案:

答案 0 :(得分:1)

使用xsl:sort

以下代码不在我的头顶,可能无效;但背后的想法应该是清楚的。

<xsl:template match="node()|@*">    <- This should copy everything as it is
    <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
</xsl:template>

<xsl:template match="grandParent/parent"> <- parent elements will copy in this order
    <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:copy-of select="a"/>
    <b>
        <xsl:for-each select="b/*">
            <xsl:sort select="text()" />
            <xsl:copy-of select="." />
        </xsl:for-each>
    </b>
    <xsl:copy-of select="c"/>
    </xsl:copy>
</xsl:template>

答案 1 :(得分:1)

  

我尝试使用另一个xsl:template for“grandParent / parent / b”,但没有帮助。

由于您拥有身份模板,因此应使用<xsl:apply-templates>代替<xsl:copy-of>

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

现在,您可以为b元素

添加类似的模板
<xsl:template match="parent/b">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="b1"/>
        <xsl:apply-templates select="b2"/>
    </xsl:copy>
</xsl:template>

这将很好地处理b不存在的情况 - 如果select="b"找不到任何元素,则不会触发任何模板。

实际上,如果两种情况下的排序顺序相同(按字​​母顺序按元素名称),那么您可以将两个模板合并为一个使用<xsl:sort>的模板,从而完全转换

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

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

  <xsl:template match="grandParent/parent | parent/b">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="*">
        <xsl:sort select="name()" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

(对于您给出的示例XML,实际上并不需要@*位,因为XML不包含任何属性,但如果存在,则不会有任何伤害是真正的XML中的任何一个,或者您将来添加任何内容。)

答案 2 :(得分:1)

这是一个最通用的解决方案 - 使用xsl:sort和模板 - 没有xsl:copy-of而且没有特定名称的硬编码

<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="*/*">
     <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="node()">
         <xsl:sort select="name()"/>
       </xsl:apply-templates>
     </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<grandParent>
    <parent>
        <c>789</c>
        <b>
            <b2>123</b2>
            <b1>456</b1>
        </b>
        <a>123</a>
    </parent>
    ....
</grandParent>

产生了想要的正确结果

<grandParent>
   <parent>
      <a>123</a>
      <b>
         <b1>456</b1>
         <b2>123</b2>
      </b>
      <c>789</c>
   </parent>
    ....
</grandParent>

现在,让我们更改XML文档中的所有名称 - 请注意,其他任何答案都不适用于此

<someGrandParent>
    <someParent>
        <z>789</z>
        <y>
            <y2>123</y2>
            <y1>456</y1>
        </y>
        <x>123</x>
    </someParent>
    ....
</someGrandParent>

我们应用相同的转换,它会再次生成正确的结果:

<someGrandParent>
   <someParent>
      <x>123</x>
      <y>
         <y1>456</y1>
         <y2>123</y2>
      </y>
      <z>789</z>
   </someParent>
    ....
</someGrandParent>