XSLT如何按名称对节点进行排序?

17

我不确定如何使用xsl:sort指令。 我需要按标记名称对元素进行排序(用于差异),但我似乎想不出如何使其工作。 我的第一反应是修改身份转换并将其修改为包含排序语句,但我不确定如何实现。

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


请提供一个小的源XML文档以及期望的转换结果。 - Dimitre Novatchev
好问题,+1。请查看我的答案,其中包括对属性进行排序的完整解决方案和一个警告。 - Dimitre Novatchev
1个回答

30

这个转换:

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

   <xsl:apply-templates select="node()">
    <xsl:sort select="name()"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

应用于此 XML 文档时

<t b="x" c="y" a="t">
  <c/>
  <b/>
  <a/>
</t>

生成所需的排序输出:

<t a="t" b="x" c="y">
    <a></a>
    <b></b>
    <c></c>
</t>

请注意:

  1. 不仅元素,而且属性也会被排序(后者依赖于实现,但在MSXML中可以正常工作)。

  2. 使用已排序的XML进行差异比较是不可靠的,因为将XML文档转换为排序表示形式不是1:1映射。


1
@Stefan-Kendall:即使唯一的区别在于元素顺序,两个XML文档也是不同的。 - Dimitre Novatchev
1
我还想在答案中指出,拆分 apply-template 是必要的,以防止 sort 描述符处理节点和属性时采用相同的序列顺序。一旦命中属性后的第一个节点,就无法处理更多属性。通过拆分排序指令,我们避免了这个问题,并获得了所需的排序。 - Stefan Kendall
@DimitreNovatchev 即使唯一的不同之处在于元素顺序,两个XML文档也是不同的。 你能给一些参考吗? - Piotr Dobrogost
如果您想在运行比较之前“清理”XML,那么这是一种非常有用的XSLT。是的,元素的顺序在语义上很重要,但实际上我们经常看到工具忽略顺序甚至生成具有“任意”顺序的XML(例如每次都不同)。这会创建DOM-wise可能相同但text-wise非常不同的XML文件(包括其他痛点,如随机缩进,不同的节点关闭样式等)。 - Martin Devillers
@MartinDevillers,确实如此。如果XML文档是PL对象的序列化,那么代表属性的元素的顺序并不重要。然而,如果XML文档是文档的表示形式,那么其元素的顺序(例如代表章节和子节)就非常重要了。 - Dimitre Novatchev
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接