代码之家  ›  专栏  ›  技术社区  ›  l0b0

如何规范XML元素和属性的顺序?

  •  0
  • l0b0  · 技术社区  · 3 年前

    我正在对一堆由第三方应用程序生成的XML文件进行版本控制。不幸的是,文件的保存方式往往会使版本控制变得过于繁琐。它们可能会交换以下元素:

     <root>
    -    <b>bar</b>
         <a>foo</a>
    +    <b>bar</b>
     </root>
    

    或重新排序属性:

    -<root a="foo" b="bar"/>
    +<root b="bar" a="foo"/>
    

    或更改/删除缩进:

    -<root a="foo" b="bar"/>
    +<root
    +  a="foo"
    +  b="bar"/>
    

    需要明确的是,这些文件不会混合文本和元素节点(如 <a>foo <b>bar</b></a> ),并且不同排序的文件之间没有语义差异,所以可以按照我们想要的任何方式对它们进行重新排序。

    我已经通过使用部分解决了这个问题 xsltproc 以及以下内容 schema 要对元素进行排序:

    <stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
        <output method="xml" indent="yes" encoding="UTF-8"/>
        <strip-space elements="*"/>
    
        <template match="processing-instruction()|@*">
            <copy>
                <apply-templates select="node()|@*"/>
            </copy>
        </template>
    
        <template match="*">
            <copy>
                <apply-templates select="@*"/>
                <apply-templates>
                    <sort select="name()"/>
                    <sort select="@*[1]"/>
                    <sort select="@*[2]"/>
                    <sort select="@*[3]"/>
                    <sort select="@*[4]"/>
                    <sort select="@*[5]"/>
                    <sort select="@*[6]"/>
                </apply-templates>
            </copy>
        </template>
    </stylesheet>
    

    然而,我最近了解到 attribute ordering is not defined ,所以按六个“第一”属性排序通常不起作用。当然,这并没有对属性进行排序。

    (我在标题中使用了“normalize”,因为我不一定想 分类 元素,这似乎是确保两个语义相同的文件之间的文本差异为空的最明显的方法。)

    有没有办法实现这样的订购?

    尽管有名字,但这与 XSLT sort by tag name and attribute value 。这个问题只包括一个属性,而且公认的解决方案不够笼统。

    0 回复  |  直到 3 年前
        1
  •  1
  •   michael.hor257k    3 年前

    这项工作的目的并不完全清楚。如果您只想“规范化”(规范化?)不同的文档,使元素及其属性以相同的顺序(和缩进)显示,您可以简单地执行以下操作:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" 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 1

    <input shape="circle" size="large" color="blue">
        <shape>circle</shape>
        <size>large</size>
        <color>blue</color>
    </input>
    

    XML 2

    <input color="red" size="small" shape="square">
        <color>red</color>
        <size>small</size>
        <shape>square</shape>
    </input>
    

    结果将分别为:

    结果1

    <?xml version="1.0" encoding="UTF-8"?>
    <input color="blue" shape="circle" size="large">
      <color>blue</color>
      <shape>circle</shape>
      <size>large</size>
    </input>
    

    结果2

    <?xml version="1.0" encoding="UTF-8"?>
    <input color="red" shape="square" size="small">
      <color>red</color>
      <shape>square</shape>
      <size>small</size>
    </input>
    

    笔记 :
    自从 the order of attributes is by definition insignificant ,XSLT处理器没有义务按照指令对它们进行排序。然而,在实践中,大多数处理器都会这样做。