我的 XSLT 传奇中的这一章是这里问题的扩展。 感谢所有帮助我走到这一步的人(@Martin Honnen,@Ian Roberts,@Tim C以及我错过的任何其他人)!
这是我目前的问题:
- 我在
A_v1.xml
中重新排序了一些兄弟姐妹以创建A_v2.xml
。 我现在认为这两个文件是同一文件的不同"版本"。 文件两个文件具有完全相同的内容,只是一些兄弟姐妹的顺序不同。 另一种说法是,A_v2.xml
中的每个元素仍然具有与A_v1.xml
相同的父元素,但它现在可能发生在它曾经发生的兄弟姐妹之前,或者可能发生在以前发生的兄弟姐妹之后。 - 我把
A_v1.xml
变成A_v1_transformed.xml
- 我把
A_v2.xml
变成A_v2_transformed.xml
- 我把
A_v1_transformed.xml
比作A_v2_transformed.xml
,令我沮丧的是,它们并不完全相同。 此外,它们都不符合expected.xml
中显示的预期顺序。 它们具有相同的内容,但元素的排序顺序不同。
我的第一个排序是<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
相同的校验和,但它们没有。 这是我最关心的问题。 按字母顺序排序似乎是最理智的排序方式,但只要转换以某种理智的方式排序,只要排序在同一文件的不同"版本"之间是可重复的,我就不在乎排序是如何发生的。
意料之中.xml
以下 XLS 文件都产生相同的结果,但"多通道"版本可能更容易理解。
xsl_concise.xsl
xsl_multi_pass.xsl
讨论要点:
- 我注意到,按字母顺序排序时,大写字母优先。 即使大写字母按字母顺序出现在小写字母之后,它也会在排序中排在第一位。
部分成功...
我想我自己可能偶然发现了部分解决方案,但我不清楚它为什么有效。 如果你看一下我的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"/>
这对元素进行了两次排序,我不知道为什么有必要。 使用我提供的示例文件的结果是我期望的减去优先的大写字母,但只要结果看起来是一致的,这就不会打扰我。 问题是这个"解决方案"导致我正在使用的实际文件的另一部分排序不一致。
成功!
我想我终于按照我想要的方式 100% 工作了。 我将 Novatchev @Dimitre 的答案中给出的函数合并到元素的属性名称和值中。 出于某种原因,我仍然必须执行两次传递来对元素进行排序(应用完全相同的模板两次),但对于 3MB 的文件只需要额外的 20 秒,所以我不太担心它。
这是最终结果:
xsl_2.0_full_document_sorter.xsl
简而言之,我所有 XSLT 问题的最终目标是样式表,当应用于文件时,即使运行在该文件的不同"版本"上,也始终会生成相同的结果。文件的不同"版本"将是具有完全相同内容的文件,只是顺序不同。这意味着元素的属性可能已被移动,并且元素可能比以前更晚地出现。
您是否考虑过使用其他工具而不是 XSLT 来实现此目的? 在我看来,您描述的目标几乎完全符合 XMLUnit 中similar()
的定义
// 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();
成功!
我想我终于按照我想要的方式 100% 工作了。 我合并了 Novatchev @Dimitre 在答案中给出的函数,以按属性名称和值对元素进行排序。 出于某种原因,我仍然必须执行两次传递来对元素进行排序(应用完全相同的模板两次),但对于 3MB 的文件只需要额外的 20 秒,所以我不太担心它。
这是最终结果:
xsl_2.0_full_document_sorter.xsl
此转换是 100% 通用的,应该能够在任何 XML 文档上使用,以我认为最合理的方式对其进行排序。 此样式表的主要优点是,它将以完全相同的方式以不同的顺序转换具有相同内容的多个文件,以具有相同内容的所有文件的转换结果将是相同的。