1

我对 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>
4

3 回答 3

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>
于 2013-03-27T12:45:21.827 回答
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>
于 2013-03-27T13:23:13.030 回答
1

我尝试将另一个 xsl:template 用于“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 中有任何属性或者您将来添加任何内容)。

于 2013-03-27T12:57:40.830 回答