【问题标题】:Compare XML ignoring order of child elements比较 XML 忽略子元素的顺序
【发布时间】:2009-11-19 22:31:14
【问题描述】:

有没有人知道可以比较两个 XML 文档的工具。避免嘲笑……还有更多。我需要一些东西来确保文件 1 中的每个节点也在文件 2 中,而不管顺序如何。我原以为 XML Spy 会使用 Ignore Order of Child Nodes 选项来做到这一点,但事实并非如此。以下将被视为相同:

<Node>
    <Child name="Alpha"/>
    <Child name="Beta"/>
    <Child name="Charlie"/>
</Node>

<Node>
    <Child name="Beta"/>
    <Child name="Charlie"/>
    <Child name="Alpha"/>
</Node>

【问题讨论】:

  • 正是我想要的。那么哪个比较工具可以处理这个重要的细微差别呢?

标签: xml compare


【解决方案1】:

我为此编写了一个简单的 Python 工具,名为 xmldiffs

比较两个 XML 文件,忽略元素和属性顺序。

用法:xmldiffs [OPTION] FILE1 FILE2

任何额外的选项都会传递给diff 命令。

https://github.com/joh/xmldiffs获取它

【讨论】:

  • 不适用于元素的文本节点顺序不同的情况,例如,A valueAnother value根>
  • @AdamC 哦,听起来像个错误...你能把问题提交到GitHub吗?
  • 非常感谢您的工具!完美运行。有一个快速修改可以适用于 Python3:github.com/joh/xmldiffs/issues/10
【解决方案2】:

使用Beyond Compare,您可以在File Formats-设置中使用XML Sort 转换。使用此选项,XML 子项将在 diff 之前排序。

Beyond Compare 的试用/便携版本是available

【讨论】:

  • 一旦你找到了这个金色的设置!
【解决方案3】:

您可能想在 Google 上搜索“XML diff tool”,这将为您提供足够多的结果。 其中之一是OxygenXml,这是我经常使用的工具。你也可以试试微软的XML Diff and Patch Tool

祝你好运。

【讨论】:

  • 谷歌搜索并没有像我希望的那样产生任何有希望的免费可执行下载。但是,在 XML 差异和补丁工具上 +1!它要求您有 Visual Studio 来构建它以获取 .exe。要获得良好的视觉格式 xml 差异,请构建 /Samples/XmlDiffView 项目,然后运行 ​​XmlDiffView [-flags] 1.xml 2.xml visual-output.html
  • OxygenXML 是否支持功能 OP 请求?它是如何配置的?
  • OxygenXml 忽略属性顺序,但似乎没有忽略子元素顺序...
  • 此时此页面是谷歌搜索diff xml ignore order of child nodes的最高结果。
【解决方案4】:

我会为此使用XMLUnit,因为它可以满足不同顺序的元素。

【讨论】:

  • 我用这种方法取得了不同程度的成功(XmlUnit 1 和 2)。它经常工作,但有时会失败(对于除了排序顺序之外明显相同的 XML 对)。
  • 如果它们真的是相同的,除了排序顺序听起来可能是一个错误。值得检查是否存在现有问题,如果您可以轻松重现它,请报告它。
  • 似乎不支持忽略元素顺序 :( github.com/xmlunit/xmlunit/issues/44
【解决方案5】:

今天晚上我也有类似的需求,但找不到符合我要求的东西。

我的解决方法是对我想要比较的两个 XML 文件进行排序,按元素名称的字母顺序排序。一旦它们的顺序一致,我就可以使用常规的视觉差异工具来区分这两个排序的文件。

如果这种方法对其他人有用,我已经在http://dalelane.co.uk/blog/?p=3225 分享了我编写的用于排序的python 脚本

【讨论】:

    【解决方案6】:

    我最近在这里给出了类似的答案 (Open source command line tool for Linux to diff XML files ignoring element order),但我会提供更多细节...

    如果您编写一个程序来一起遍历两棵树,您可以自定义逻辑来识别树之间的“匹配”,以及处理不匹配的节点。这是 xslt 2.0 中的一个示例(对不起,它太长了):

    <xsl:stylesheet version="2.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    
                    xmlns:set="http://exslt.org/sets"
    
                    xmlns:primary="primary"
                    xmlns:control="control"
    
                    xmlns:util="util"
    
                    exclude-result-prefixes="xsl xs set primary control">
    
        <!-- xml diff tool
    
             import this stylesheet from another and call the "compare" template with two args:
    
                 primary: the root of the primary tree to submit to comparison
                 control: the root of the control tree to compare against
    
             the two trees will be walked together. the primary tree will be walked in document order, matching elements
             and attributes from the control tree along the way, building a tree of common content, with appendages
             containing primary and control only content. that tree will then be used to generate the diff.
    
             the process of matching involves finding, for an element or attribute in the primary tree, the
             equivalent element or attribute in the control tree, *at the same level*, and *regardless of ordering*.
    
                 matching logic is encoded as templates with mode="find-match", providing a hook to wire in specific
                 matching logic for particular elements or attributes. for example, an element may "match" based on an
                 @id attribute value, irrespective of element ordering; encode this in a mode="find-match" template.
    
                 the treatment of diffs is encoded as templates with mode="primary-only" and "control-only", providing
                 hooks for alternate behavior upon encountering differences.
    
              -->
    
        <xsl:output method="text"/>
    
        <xsl:strip-space elements="*"/>
    
        <xsl:param name="full" select="false()"/><!-- whether to render the full doc, as opposed to just diffs -->
    
        <xsl:template match="/">
            <xsl:call-template name="compare">
                <xsl:with-param name="primary" select="*/*[1]"/><!-- first child of root element, for example -->
                <xsl:with-param name="control" select="*/*[2]"/><!-- second child of root element, for example -->
            </xsl:call-template>
        </xsl:template>
    
        <!-- OVERRIDES: templates that can be overridden to provide targeted matching logic and diff treatment -->
    
        <!-- default find-match template for elements
             (by default, for "complex" elements, name has to match, for "simple" elements, name and value do)
             for context node (from "primary"), choose from among $candidates (from "control") which one matches
             (override with more specific match patterns to effect alternate behavior for targeted elements)
             -->
        <xsl:template match="*" mode="find-match" as="element()?">
            <xsl:param name="candidates" as="element()*"/>
            <xsl:choose>
                <xsl:when test="text() and count(node()) = 1"><!-- simple content -->
                    <xsl:sequence select="$candidates[node-name(.) = node-name(current())][text() and count(node()) = 1][. = current()][1]"/>
                </xsl:when>
                <xsl:when test="not(node())"><!-- empty -->
                    <xsl:sequence select="$candidates[node-name(.) = node-name(current())][not(node())][1]"/>
                </xsl:when>
                <xsl:otherwise><!-- presumably complex content -->
                    <xsl:sequence select="$candidates[node-name(.) = node-name(current())][1]"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
        <!-- default find-match template for attributes
             (by default, name and value have to match)
             for context attr (from "primary"), choose from among $candidates (from "control") which one matches
             (override with more specific match patterns to effect alternate behavior for targeted attributes)
             -->
        <xsl:template match="@*" mode="find-match" as="attribute()?">
            <xsl:param name="candidates" as="attribute()*"/>
            <xsl:sequence select="$candidates[. = current()][node-name(.) = node-name(current())][1]"/>
        </xsl:template>
    
        <!-- default primary-only template (override with more specific match patterns to effect alternate behavior) -->
        <xsl:template match="@* | *" mode="primary-only">
            <xsl:apply-templates select="." mode="illegal-primary-only"/>
        </xsl:template>
    
        <!-- write out a primary-only diff -->
        <xsl:template match="@* | *" mode="illegal-primary-only">
            <primary:only>
                <xsl:copy-of select="."/>
            </primary:only>
        </xsl:template>
    
        <!-- default control-only template (override with more specific match patterns to effect alternate behavior) -->
        <xsl:template match="@* | *" mode="control-only">
            <xsl:apply-templates select="." mode="illegal-control-only"/>
        </xsl:template>
    
        <!-- write out a control-only diff -->
        <xsl:template match="@* | *" mode="illegal-control-only">
            <control:only>
                <xsl:copy-of select="."/>
            </control:only>
        </xsl:template>
    
        <!-- end OVERRIDES -->
    
        <!-- MACHINERY: for walking the primary and control trees together, finding matches and recursing -->
    
        <!-- compare "primary" and "control" trees (this is the root of comparison, so CALL THIS ONE !) -->
        <xsl:template name="compare">
            <xsl:param name="primary"/>
            <xsl:param name="control"/>
    
            <!-- write the xml diff into a variable -->
            <xsl:variable name="diff">
                <xsl:call-template name="match-children">
                    <xsl:with-param name="primary" select="$primary"/>
                    <xsl:with-param name="control" select="$control"/>
                </xsl:call-template>
            </xsl:variable>
    
            <!-- "print" the xml diff as textual output -->
            <xsl:apply-templates select="$diff" mode="print">
                <xsl:with-param name="render" select="$full"/>
            </xsl:apply-templates>
    
        </xsl:template>
    
        <!-- assume primary (context) element and control element match, so render the "common" element and recurse -->
        <xsl:template match="*" mode="common">
            <xsl:param name="control"/>
    
            <xsl:copy>
                <xsl:call-template name="match-attributes">
                    <xsl:with-param name="primary" select="@*"/>
                    <xsl:with-param name="control" select="$control/@*"/>
                </xsl:call-template>
    
                <xsl:choose>
                    <xsl:when test="text() and count(node()) = 1">
                        <xsl:value-of select="."/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="match-children">
                            <xsl:with-param name="primary" select="*"/>
                            <xsl:with-param name="control" select="$control/*"/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:copy>
    
        </xsl:template>
    
        <!-- find matches between collections of attributes in primary vs control -->
        <xsl:template name="match-attributes">
            <xsl:param name="primary" as="attribute()*"/>
            <xsl:param name="control" as="attribute()*"/>
            <xsl:param name="primaryCollecting" as="attribute()*"/>
    
            <xsl:choose>
                <xsl:when test="$primary and $control">
                    <xsl:variable name="this" select="$primary[1]"/>
                    <xsl:variable name="match" as="attribute()?">
                        <xsl:apply-templates select="$this" mode="find-match">
                            <xsl:with-param name="candidates" select="$control"/>
                        </xsl:apply-templates>
                    </xsl:variable>
    
                    <xsl:choose>
                        <xsl:when test="$match">
                            <xsl:copy-of select="$this"/>
                            <xsl:call-template name="match-attributes">
                                <xsl:with-param name="primary" select="subsequence($primary, 2)"/>
                                <xsl:with-param name="control" select="remove($control, 1 + count(set:leading($control, $match)))"/>
                                <xsl:with-param name="primaryCollecting" select="$primaryCollecting"/>
                            </xsl:call-template>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:call-template name="match-attributes">
                                <xsl:with-param name="primary" select="subsequence($primary, 2)"/>
                                <xsl:with-param name="control" select="$control"/>
                                <xsl:with-param name="primaryCollecting" select="$primaryCollecting | $this"/>
                            </xsl:call-template>
                        </xsl:otherwise>
                    </xsl:choose>
    
                </xsl:when>
                <xsl:otherwise>
                    <xsl:if test="$primaryCollecting | $primary">
                        <xsl:apply-templates select="$primaryCollecting | $primary" mode="primary-only"/>
                    </xsl:if>
                    <xsl:if test="$control">
                        <xsl:apply-templates select="$control" mode="control-only"/>
                    </xsl:if>
                </xsl:otherwise>
            </xsl:choose>
    
        </xsl:template>
    
        <!-- find matches between collections of elements in primary vs control -->
        <xsl:template name="match-children">
            <xsl:param name="primary" as="node()*"/>
            <xsl:param name="control" as="element()*"/>
    
            <xsl:variable name="this" select="$primary[1]" as="node()?"/>
    
            <xsl:choose>
                <xsl:when test="$primary and $control">
                    <xsl:variable name="match" as="element()?">
                        <xsl:apply-templates select="$this" mode="find-match">
                            <xsl:with-param name="candidates" select="$control"/>
                        </xsl:apply-templates>
                    </xsl:variable>
    
                    <xsl:choose>
                        <xsl:when test="$match">
                            <xsl:apply-templates select="$this" mode="common">
                                <xsl:with-param name="control" select="$match"/>
                            </xsl:apply-templates>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:apply-templates select="$this" mode="primary-only"/>
                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:call-template name="match-children">
                        <xsl:with-param name="primary" select="subsequence($primary, 2)"/>
                        <xsl:with-param name="control" select="if (not($match)) then $control else remove($control, 1 + count(set:leading($control, $match)))"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:when test="$primary">
                    <xsl:apply-templates select="$primary" mode="primary-only"/>
                </xsl:when>
                <xsl:when test="$control">
                    <xsl:apply-templates select="$control" mode="control-only"/>
                </xsl:when>
            </xsl:choose>
    
        </xsl:template>
    
        <!-- end MACHINERY -->
    
        <!-- PRINTERS: print templates for writing out the diff -->
    
        <xsl:template match="*" mode="print">
            <xsl:param name="depth" select="-1"/>
            <xsl:param name="render" select="false()"/>
            <xsl:param name="lineLeader" select="' '"/>
            <xsl:param name="rest" as="element()*"/>
    
            <xsl:if test="$render or descendant::primary:* or descendant::control:*">
    
                <xsl:call-template name="whitespace">
                    <xsl:with-param name="indent" select="$depth"/>
                    <xsl:with-param name="leadChar" select="$lineLeader"/>
                </xsl:call-template>
                <xsl:text>&lt;</xsl:text>
                <xsl:value-of select="name(.)"/>
    
                <xsl:apply-templates select="@* | primary:*[@*] | control:*[@*]" mode="print">
                    <xsl:with-param name="depth" select="$depth"/>
                    <xsl:with-param name="render" select="$render"/>
                    <xsl:with-param name="lineLeader" select="$lineLeader"/>
                </xsl:apply-templates>
    
                <xsl:choose>
                    <xsl:when test="text() and count(node()) = 1"><!-- field element (just textual content) -->
                        <xsl:text>&gt;</xsl:text>
                        <xsl:value-of select="."/>
                        <xsl:text>&lt;/</xsl:text>
                        <xsl:value-of select="name(.)"/>
                        <xsl:text>&gt;</xsl:text>
                    </xsl:when>
                    <xsl:when test="count(node()) = 0"><!-- empty (self-closing) element -->
                        <xsl:text>/&gt;</xsl:text>
                    </xsl:when>
                    <xsl:otherwise><!-- complex content -->
                        <xsl:text>&gt;&#10;</xsl:text>
                        <xsl:apply-templates select="*[not(self::primary:* and @*) and not(self::control:* and @*)]" mode="print">
                            <xsl:with-param name="depth" select="$depth + 1"/>
                            <xsl:with-param name="render" select="$render"/>
                            <xsl:with-param name="lineLeader" select="$lineLeader"/>
                        </xsl:apply-templates>
                        <xsl:call-template name="whitespace">
                            <xsl:with-param name="indent" select="$depth"/>
                            <xsl:with-param name="leadChar" select="$lineLeader"/>
                        </xsl:call-template>
                        <xsl:text>&lt;/</xsl:text>
                        <xsl:value-of select="name(.)"/>
                        <xsl:text>&gt;</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
    
                <xsl:text>&#10;</xsl:text>
    
            </xsl:if>
    
            <!-- write the rest of the elements, if any -->
            <xsl:apply-templates select="$rest" mode="print">
                <xsl:with-param name="depth" select="$depth"/>
                <xsl:with-param name="render" select="$render"/>
                <xsl:with-param name="lineLeader" select="$lineLeader"/>
                <xsl:with-param name="rest" select="()"/><!-- avoid implicit param pass to recursive call! -->
            </xsl:apply-templates>
    
        </xsl:template>
    
        <xsl:template match="@*" mode="print">
            <xsl:param name="depth" select="0"/>
            <xsl:param name="render" select="false()"/>
            <xsl:param name="lineLeader" select="' '"/>
            <xsl:param name="rest" as="attribute()*"/>
    
            <xsl:if test="$render">
    
                <xsl:text>&#10;</xsl:text>
                <xsl:call-template name="whitespace">
                    <xsl:with-param name="indent" select="$depth + 3"/>
                    <xsl:with-param name="leadChar" select="$lineLeader"/>
                </xsl:call-template>
                <xsl:value-of select="name(.)"/>
                <xsl:text>="</xsl:text>
                <xsl:value-of select="."/>
                <xsl:text>"</xsl:text>
            </xsl:if>
    
            <xsl:apply-templates select="$rest" mode="print">
                <xsl:with-param name="depth" select="$depth"/>
                <xsl:with-param name="render" select="$render"/>
                <xsl:with-param name="lineLeader" select="$lineLeader"/>
                <xsl:with-param name="rest" select="()"/><!-- avoid implicit param pass to recursive call! -->
            </xsl:apply-templates>
    
        </xsl:template>
    
        <xsl:template match="primary:* | control:*" mode="print">
            <xsl:param name="depth"/>
    
            <xsl:variable name="diffType" select="util:diff-type(.)"/>
            <xsl:variable name="primary" select="self::primary:*"/>
            <xsl:variable name="lineLeader" select="if ($primary) then '+' else '-'"/>
    
            <!-- only if this is the first in a sequence of control::* elements, since the rest are handled along with the first... -->
            <xsl:if test="util:diff-type(preceding-sibling::*[1]) != $diffType">
                <xsl:if test="@*">
                    <xsl:text>&#10;</xsl:text>
                </xsl:if>
                <xsl:call-template name="diffspace">
                    <xsl:with-param name="indent" select="if (@*) then $depth + 3 else $depth"/>
                    <xsl:with-param name="primary" select="$primary"/>
                </xsl:call-template>
                <b><i>&lt;!-- ... --&gt;</i></b><!-- something to identify diff sections in output -->
                <xsl:if test="node()">
                    <xsl:text>&#10;</xsl:text>
                </xsl:if>
                <xsl:variable name="rest" select="set:leading(following-sibling::*, following-sibling::*[util:diff-type(.) != $diffType])"/>
                <xsl:apply-templates select="@* | node()" mode="print">
                    <xsl:with-param name="depth" select="$depth"/>
                    <xsl:with-param name="render" select="true()"/>
                    <xsl:with-param name="lineLeader" select="$lineLeader"/>
                    <xsl:with-param name="rest" select="$rest/@* | $rest/*"/>
                </xsl:apply-templates>
            </xsl:if>
        </xsl:template>
    
        <xsl:template name="whitespace">
            <xsl:param name="indent" select="0" as="xs:integer"/>
            <xsl:param name="leadChar" select="' '"/>
            <xsl:choose>
                <xsl:when test="$indent > 0">
                    <xsl:value-of select="$leadChar"/>
                    <xsl:text> </xsl:text>
                    <xsl:for-each select="0 to $indent - 1">
                        <xsl:text>  </xsl:text>
                    </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:for-each select="0 to $indent">
                        <xsl:text>  </xsl:text>
                    </xsl:for-each>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
        <xsl:template name="diffspace">
            <xsl:param name="indent" select="0" as="xs:integer"/>
            <xsl:param name="primary" select="false()"/>
            <xsl:for-each select="0 to $indent">
                <xsl:choose>
                    <xsl:when test="$primary">
                        <xsl:text>++</xsl:text>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>--</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:template>
    
        <!-- just an "enum" for deciding whether to group adjacent diffs -->
        <xsl:function name="util:diff-type" as="xs:integer">
            <xsl:param name="construct"/>
            <xsl:sequence select="if ($construct/self::primary:*[@*]) then 1 else
                                  if ($construct/self::control:*[@*]) then 2 else
                                  if ($construct/self::primary:*) then 3 else
                                  if ($construct/self::control:*) then 4 else
                                  if ($construct) then 5 else 0"/>
        </xsl:function>
    
        <!-- end PRINTERS -->
    
    </xsl:stylesheet>
    

    根据您的输入考虑这个示例输入:

    <test>
        <Node>
            <Child name="Alpha"/>
            <Child name="Beta"/>
            <Child name="Charlie"/>
        </Node>
        <Node>
            <Child name="Beta"/>
            <Child name="Charlie"/>
            <Child name="Alpha"/>
        </Node>
    </test>
    

    按照原样的样式表,以下是应用于示例时的输出:

    <Node>
      <Child
    ++++++++<!-- ... -->
    +       name="Alpha"
    --------<!-- ... -->
    -       name="Beta">
      </Child>
      <Child
    ++++++++<!-- ... -->
    +       name="Beta"
    --------<!-- ... -->
    -       name="Charlie">
      </Child>
      <Child
    ++++++++<!-- ... -->
    +       name="Charlie"
    --------<!-- ... -->
    -       name="Alpha">
      </Child>
    </Node>
    

    但是,如果您添加此自定义模板:

    <xsl:template match="Child" mode="find-match" as="element()?">
        <xsl:param name="candidates" as="element()*"/>
        <xsl:sequence select="$candidates[@name = current()/@name][1]"/>
    </xsl:template>
    

    表示根据 @name 属性匹配 Child 元素,然后您不会得到任何输出(意味着没有差异)。

    【讨论】:

      【解决方案7】:

      这是使用 SWI-Prolog 的差异解决方案

      :- use_module(library(xpath)).
      load_trees(XmlRoot1, XmlRoot2) :-
          load_xml('./xml_source_1.xml', XmlRoot1, _),
          load_xml('./xml_source_2.xml', XmlRoot2, _).
      
      find_differences(Reference, Root1, Root2) :-
          xpath(Root1, //'Child'(@name=Name), Node),
          not(xpath(Root2, //'Child'(@name=Name), Node)),
          writeln([Reference, Name, Node]).
      
      diff :-
          load_trees(Root1, Root2),
          (find_differences('1', Root1, Root2) ; find_differences('2', Root2, Root1)).
      

      Prolog 将统一 Name 变量以匹配文件 1 和文件 2 中的节点。 对 Node 变量的统一进行“差异”检测。

      下面是一些示例输出:

      % file 1 and file 2 have no differences 
      ?- diff.
      false.
      
      % "Alpha" was updated  in file 2
      ?- diff.
      [1,Alpha,element(Child,[name=Alpha],[])]
      [2,Alpha,element(Child,[name=Alpha,age=7],[])]
      false.
      

      【讨论】:

        【解决方案8】:

        使用 C# 你可以这样做,然后将其与任何差异工具进行比较。

        public void Run()
        {
            LoadSortAndSave(@".. first file ..");
            LoadSortAndSave(@".. second file ..");
        }
        
        public void LoadSortAndSave(String path)
        {
            var xdoc = XDocument.Load(path);
            SortXml(xdoc.Root);
            File.WriteAllText(path + ".sorted", xdoc.ToString());
        }
        
        private void SortXml(XContainer parent)
        {
            var elements = parent.Elements()
                .OrderBy(e => e.Name.LocalName)
                .ToArray();
        
            Array.ForEach(elements, e => e.Remove());
        
            foreach (var element in elements)
            {
                parent.Add(element);
                SortXml(element);
            }
        }
        

        【讨论】:

          【解决方案9】:

          为此编写了一个简单的 java 程序。将两个正在比较的 XML 存储在 HashMap 中,键作为元素的 XPath(包括元素的文本值),值作为该元素的出现次数。然后比较两个 HashMap 的键集和值。

          /** * 创建具有文本值且没有嵌套节点的元素映射。
          * 这里地图的键是元素的 XPATH 与元素的文本值连接,元素的值是该元素的出现次数。
          * * @param xml内容 * @返回 * @throws ParserConfigurationException * @throws SAXException * @throws IOException */

          private static Map<String, Long> getMapOfElementsOfXML(String xmlContent)
          
                  throws ParserConfigurationException, SAXException, IOException {
          
              DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          
              dbf.setValidating(false);
          
              DocumentBuilder db = dbf.newDocumentBuilder();
          
              Document doc1 = db.parse(new ByteArrayInputStream(xmlContent.getBytes()));
          
              NodeList entries = doc1.getElementsByTagName("*");
          
              Map<String, Long> mapElements = new HashMap<>();
          
              for (int i = 0; i < entries.getLength(); i++) {
          
                  Element element = (Element) entries.item(i);
          
                  if (element.getChildNodes().getLength() == 1&&element.getTextContent()!=null) {
          
                      final String elementWithXPathAndValue = getXPath(element.getParentNode())
          
                              + "/"
          
                              + element.getParentNode().getNodeName()
          
                              + "/"
          
                              + element.getTagName()
          
                              + "/"
          
                              + element.getTextContent();
          
                      Long countValue = mapElements.get(elementWithXPathAndValue);
          
                      if (countValue == null) {
          
                          countValue = Long.valueOf(0l);
          
                      } else {
          
                          ++countValue;
          
                      }
          
                      mapElements.put(elementWithXPathAndValue, countValue);
          
                  }
          
              }
          
              return mapElements;
          
          }
          
          static String getXPath(Node node) {
          
              Node parent = node.getParentNode();
          
              if (parent == null) {
          
                  return "";
          
              }
          
              return getXPath(parent) + "/" + parent.getNodeName();
          
          }
          

          完整程序在这里https://comparetwoxmlsignoringstanzaordering.blogspot.com/2018/12/java-program-to-compare-two-xmls.html

          【讨论】:

            【解决方案10】:

            这样做的简单方法是使用版本控制工具,如乌龟 git。

            1. 创建一个github账号
            2. 在您的 git 帐户中创建一个 git 存储库
            3. 签出该存储库
            4. 添加要比较的文件的另一端
            5. 将内容推送到服务器
            6. 用剩下的一面改变来源
            7. 将您的内容与任何源文件进行比较

            【讨论】:

              【解决方案11】:

              您可以使用 Idea Intellij 中的“pom sorter”插件,并使用 Intellij 自己的“比较文件”工具。

              pom 排序器插件的市场链接:https://plugins.jetbrains.com/plugin/7084-pom-sorter

              【讨论】:

                【解决方案12】:
                /**
                     * @author sdiallo
                     * @since 2017-01-16
                     * <p>
                     * Compare the content of two XML file
                     * </p>
                     * <ul>
                     * <li>Ignore the white space</li>
                     * <li>Ignore the attribute order</li>
                     * <li>Ignore the comment</li>
                     * <li>Ignore Sequence child nodes are not the same</li>
                     * <ul>
                     * 
                     * @param String XML
                     *            first Content to be compared
                     * @param String XML
                     *            second Content to be compared
                     * @return List the differences computed between the two files
                     *         <ul>
                     *         <li>null means the files are equal</li>         
                     *         <li>elsewhere the files are different</li>
                     *         <ul>
                     * */
                    public static List buildDiffXMLs(String xmlExpected, String xmlGenerated) {
                        List<?> differencesList = null;
                
                    XMLUnit.setIgnoreAttributeOrder(true);
                    XMLUnit.setIgnoreComments(true);
                    XMLUnit.setIgnoreWhitespace(true);
                
                    try {
                        DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(
                                xmlExpected, xmlGenerated));
                
                        // Two documents are considered to be "similar" if they contain the
                        // same elements and attributes regardless of order.
                        if ( !diff.identical() && !diff.similar()) {
                            differencesList = diff.getAllDifferences();
                        }// end if
                
                    } catch (SAXException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                
                    return differencesList;
                }// buildDiffXMLs
                

                【讨论】:

                • 提示:请花费 1 分钟来正确格式化您的代码的所有;并且可能会在那里进行初步解释。
                【解决方案13】:

                作为一种(非常)快速而肮脏的方法,我在紧要关头做到了这一点:

                1. 打开 Excel
                2. 将文件 1 粘贴到 A 列,每行一行。将范围命名为“FILE1”
                3. 将文件 2 粘贴到 B 列中,每行一行。将范围命名为“FILE2”
                4. 在C1中,输入公式:

                  =IF(ISERROR(VLOOKUP(B1,FILE1,1,FALSE)),"DIFF","")
                  
                5. 在D1,进入论坛:

                  =IF(ISERROR(VLOOKUP(A1,FILE2,1,FALSE)),"DIFF","")
                  
                6. 将列 C 和 D 填写到文件的底部。

                这将突出显示出现在一个文件中而不是另一个文件中的任何行。它一点也不整洁,但有时你只需要处理你所拥有的东西。

                【讨论】:

                  猜你喜欢
                  • 2023-03-21
                  • 2016-11-01
                  • 2018-08-16
                  • 1970-01-01
                  • 2013-05-08
                  • 1970-01-01
                  • 2012-10-03
                  • 2010-11-17
                  • 2020-01-19
                  相关资源
                  最近更新 更多