重访:递归地对任意XML文档的元素进行排序

时间:2013-09-16 19:38:38

标签: xslt

我的XSLT传奇中的这一章是问题here的扩展。感谢所有帮助我做到这一点的人(@Martin Honnen,@ Ian Roberts,@ Tim C,以及我错过的任何人)!

这是我目前的问题:

  1. 我在A_v1.xml中为一些兄弟姐妹重新排序以创建A_v2.xml我现在认为这两个文件是同一文件的不同“版本”。两个文件的文件具有完全相同的内容,只有一些兄弟的顺序不同。换句话说,A_v2.xml中的每个元素仍然具有与A_v1.xml中相同的父元素,但它现在可能发生在以前的兄弟姐妹之后,或者可能发生在之后的兄弟姐妹之后发生在之前。
  2. 我将A_v1.xml转换为A_v1_transformed.xml
  3. 我将A_v2.xml转换为A_v2_transformed.xml
  4. 我将A_v1_transformed.xmlA_v2_transformed.xml进行比较,令我沮丧的是,他们并不相同。更进一步,它们都不是expected.xml中所示的预期顺序。它们具有相同的内容,但元素的排序顺序不同。
  5. 我的第一个排序是<xsl:sort select="local-name()"/>@G. Ken Holman将我转到<xsl:sort select="."/>(与我使用的<xsl:sort select="self::*"/>效果相同)。当我组合使用这两种排序时,我几乎可以得到我想要的东西,但在某些地方,似乎预期的字母顺序只是随机破坏。

    我加强了我的示例文件。为了使问题简短,我只需将它们放在pastebin上。

    A_v1.xml

    A_v2.xml

    A_v1_transformed.xml

    A_v2_transformed.xml

    以下是我添加了评论的转换文件之一,以帮助您了解我认为转换错误地对这些文件进行排序的位置/原因。我没有评论其他转换文件,因为它有类似的“失败”。

    A_v1_transformed_with_comments.xml

    两个转换后的文档都应该与expected.xml具有相同的校验和,但它们不会。 这是我最关心的问题。按字母顺序排序似乎是最理智的排序方式,但只要变换按照一些理智的方式排序我就不在乎排序的发生方式只要排序在同一文件的不同“版本”之间是可重复的。

    expected.xml

    以下XLS文件都会产生相同的结果,但“多遍”版本可能更容易理解。

    xsl_concise.xsl

    xsl_multi_pass.xsl

    讨论要点:

    1. 我注意到按字母顺序排序时,CAPITALIZED字母优先。即使大写字母按字母顺序排在小写字母之后,它也会排在第一位。
    2. 部分成功......

      我想我自己可能偶然发现了部分解决方案,但我不清楚它为什么会起作用。如果您查看我的xsl_multi_pass.xsl文件,您会看到:

          <!-- Third pass with sortElements mode templates -->
          <xsl:variable name="sortElementsRslt">
              <xsl:apply-templates mode="sortElements" select="$sortAttributesRslt"/>
          </xsl:variable>
      
          <!-- Fourth pass with deDup mode templates -->
          <xsl:apply-templates mode="deDup" select="$sortElementsRslt"/>
      

      如果我把它变成:

          <!-- Third pass with sortElements mode templates -->
          <xsl:variable name="sortElementsRslt1">
              <xsl:apply-templates mode="sortElements" select="$sortAttributesRslt"/>
          </xsl:variable>
      
          <!-- Fourth pass with sortElements mode templates -->
          <xsl:variable name="sortElementsRslt2">
              <xsl:apply-templates mode="sortElements" select="$sortElementsRslt1"/>
          </xsl:variable>
      
          <!-- Fifth pass with deDup mode templates -->
          <xsl:apply-templates mode="deDup" select="$sortElementsRslt2"/>
      

      这对元素进行了两次排序,我不知道为什么有必要。使用我提供的示例文件的结果是我所期望的减去CAPITALIZED字母优先,但只要结果看起来是一致的,这不会打扰我。问题是这个“解决方案”导致我正在使用的另一部分真实文件被排序不一致。

      SUCCESS!

      我想我终于100%按照自己的意愿工作了。我通过here将答案@Dimitre Novatchev中给出的函数的属性名称和值合并到元素中。我仍然需要执行两次传递来对元素进行排序(应用完全相同的模板两次),如上所述由于某种原因,但是在20MB文件上只需要额外的3秒,所以我不太担心它。

      以下是最终结果:

      xsl_2.0_full_document_sorter.xsl

2 个答案:

答案 0 :(得分:1)

  

简而言之,我对所有XSLT问题的最终目标是一个样式表,当应用于文件时,即使在该文件的不同“版本”上运行,也会始终生成相同的结果。文件的不同“版本”将是具有完全相同内容的文件,只是以不同的顺序。这意味着元素的属性可能已被移动,并且元素可能比以前更早地发生在预告片中。

您为此目的考虑过不同的工具而不是XSLT吗?您描述的目标听起来几乎完全是similar()XMLUnit的定义

// control and test are the two XML documents you want to compare, they can
// be String, Reader, org.w3c.dom.Document or org.xml.sax.InputSource
Diff d = new Diff(control, test);
assert d.similar();

答案 1 :(得分:0)

<强> SUCCESS!

我想我终于100%按照自己的意愿工作了。我将here中给出的函数包含在@Dimitre Novatchev中,以按元素名称和值对元素进行排序。我仍然需要执行两次传递来对元素进行排序(应用完全相同的模板两次),如上所述由于某种原因,但是在20MB文件上只需要额外的3秒,所以我不太担心它。

以下是最终结果:

xsl_2.0_full_document_sorter.xsl

此转换是100%通用的,应该可以在任何XML文档上使用,以我认为最可靠的方式对其进行排序。此样式表的主要好处是,它将以完全相同的方式转换具有相同内容的不同订单中的多个文件,并且具有相同内容的所有文件的转换结果将是相同的。