XML Diff:如何使用XSLT生成XML diff?

时间:2009-11-20 15:44:54

标签: xml xslt diff

我想使用XSL / XSLT计算两个XML文件或节点之间的差异。是否有现成的样式表或任何简单的方法?

8 个答案:

答案 0 :(得分:6)

有趣的问题!我曾经试图做一些涉及两个XML源的类似事情,而我的经验是,没有办法。

您可以使用XSL的工具来包含用户构建的功能,并编写一些非常灵活的代码。但我真的看不到它。

如果我要做这样的事情,我会使用DOM4J并行处理这两个XML文件,这样我就可以轻松地以编程方式遍历代码并详细说明子查询。

尝试在XSLT中执行此操作将证明您是一个天才或让您陷入疯狂。

答案 1 :(得分:2)

这不是一个谜!以下是一般步骤:

  1. @carillonator关于XSLT如何处理文档是正确的。因此,为了更容易,我们将文档的两个版本组合到一个文档中,您可以使用它来运行XSLT差异(您可以通过命令行使用bash,或者使用您正在使用的任何编程语言,甚至是另一个XSLT转换[pipe])。这只是一个封装:

    <diff_container>
        <version1>
          ... first version here
        </version1>
        <version2>
          ... second version here
        </version2>
    </diff_container>
    
  2. 然后我们通过XSLT diff运行这个文档,然后XSLT可以简单地遍历树并比较两个版本之间的节点。这可以从非常简单(一个元素改变?移动?删除?)变为半复杂。很好地理解XPath使得这很简单。

    就像之前所说的那样,你在不同的环境中工作,所以与Diff Dog等工具相比,你是有限的。然而,在XSLT中使用该算法的好处也具有实际价值。

  3. 希望这有帮助。干杯!

答案 2 :(得分:2)

如果diff的意思是检查一个文档(或节点)中是否存在项目而不是另一个文档(或节点),则可以使用带有第三个参数的xpath key()函数

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs ="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xsl xs">

<xsl:param name="doc2diff" required="yes"/>
<!-- docB is root node of the "second" document -->
<xsl:variable name="docB" select="document($doc2diff)"/>
<!-- docA is the root node of the first document -->
<xsl:variable name="docA" select="/"/>
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:key name="items" match="Item" use="someId"/>

<xsl:template match="/">
 <ListOfItems>
  <In_A_NotIn_B>
   <xsl:apply-templates select="Item">
    <xsl:with-param name="otherDocument" select="$docB"/>
   </xsl:apply-templates>
  </In_A_NotIn_B>
  <In_B_NotIn_A>
   <xsl:apply-templates select="Item">
    <xsl:with-param name="otherDocument" select="$docA"/>
   </xsl:apply-templates>
  </In_B_NotIn_A>
 </ListOfItems>
</xsl:template>

<xsl:template match="Item">
 <xsl:param name="otherDocument"/>
  <xsl:variable name="SOMEID" select="someId"/>
  <xsl:if test="empty(key('items', $SOMEID, $otherDocument))">
   <xsl:copy-of select="."/>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>`

答案 3 :(得分:2)

XSLT是数据驱动的,也就是说,它从上到下遍历单个源XML文件,在XSL样式表中查找模板匹配。模板并不真正知道它们在数据中的位置,它们只是在匹配时运行它们的代码。您可以引用另一个XML源,但程序将根据原始源的遍历运行。

因此,当您到达<blarg>的第n个子元素时,您可以使用<blarg>函数在第二个XML中查找document()的第n个子元素。但这样做的有用性取决于XML的结构以及您尝试进行的比较。

这种行为与大多数传统脚本相反,后者从上到下运行程序代码,在指示时调用数据文件。后者 - 拉取处理 - 是您可能需要比较两个XML源。只要存在差异,XSLT就会在比较中崩溃。

答案 4 :(得分:2)

这是我编写的样式表,用于比较节点和属性中不同顺序的两个XML文件,它将生成两个包含所有叶节点路径的有序列表的文本文件。使用任何文本比较工具来发现差异或增强XSLT以执行您想要的操作。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text" indent="no" omit-xml-declaration="yes" name="output" />

<xsl:param name="OTHERFILENAME">xml_file_to_diff.xml</xsl:param>
<xsl:param name="ORIGINAL_OUTPUT_FILENAME">ORIGINAL.txt</xsl:param>
<xsl:param name="OTHER_OUTPUT_FILENAME">OTHER.txt</xsl:param>

<xsl:template match="/">
    <xsl:call-template name="convertXMLHierarchyToFullPath">
        <xsl:with-param name="node" select="*"/>
        <xsl:with-param name="filename" select="$ORIGINAL_OUTPUT_FILENAME"/>
    </xsl:call-template>
    <xsl:call-template name="convertXMLHierarchyToFullPath">
        <xsl:with-param name="node" select="document($OTHERFILENAME)/*"/>
        <xsl:with-param name="filename" select="$OTHER_OUTPUT_FILENAME"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="convertXMLHierarchyToFullPath">
    <xsl:param name="node"/>
    <xsl:param name="filename"/>

    <xsl:variable name="unorderedFullPath">
        <xsl:apply-templates select="$node"/>
    </xsl:variable>

    <xsl:result-document href="{$filename}" format="output">
        <xsl:for-each select="$unorderedFullPath/*">
            <xsl:sort select="@path" data-type="text"/>
            <xsl:value-of select="@path"/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:for-each>
    </xsl:result-document>
</xsl:template>

<xsl:template match="*">
    <xsl:if test="not(*)">
        <leaf>
            <xsl:attribute name="path">
                <xsl:for-each select="ancestor-or-self::*">
                    <xsl:value-of select="name()"/>
                    <xsl:for-each select="@*">
                        <xsl:sort select="name()" data-type="text"/>
                        <xsl:text>[</xsl:text>
                        <xsl:value-of select="name()"/>
                        <xsl:text>:</xsl:text>
                        <xsl:value-of select="."/>
                        <xsl:text>]</xsl:text>
                    </xsl:for-each>
                    <xsl:text>/</xsl:text>
                </xsl:for-each>
                <xsl:value-of select="."/>
            </xsl:attribute>
        </leaf>
    </xsl:if>
    <xsl:apply-templates select="*"/>
</xsl:template>

答案 5 :(得分:1)

最近发现这篇文章,但无论如何我会分享我的解决方案来解决这类问题。我有与@Vincent相同的需求:比较2个不同的XML文件,并快速查看它们之间的差异。快速差异有很多行匹配,因为文件没有排序,所以我决定使用XSLT对文件进行排序,然后使用WinMerge手动比较两个xml文件(一个简单的unix diff也可以完成这项工作)。

以下是对我的XML文件进行排序的XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

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

</xsl:stylesheet>

答案 6 :(得分:1)

有很多方法可以做到这一点,但我不会说这很简单。

过去我使用了一个名为diffmk的开源实用程序,这会生成一个带有额外标签的输出XML,显示已添加/删除的内容......

我必须编写一个额外的样式表,然后将其转换为更易读的HTML报告。

像XMLSpy Diff dog这样的差异工具很好,但成本很高。

答案 7 :(得分:0)

这是我最近写的一个Microsoft XSLT 1.0解决方案。它是一个无序的xml和属性比较,它返回b.xml中变量file2中找不到的输入文件中的项。如果您更改节点的顺序而不更改任何值,则不会认为存在差异。在我的github https://github.com/sflynn1812/xslt-diff-turbo

上查看