如何用Python检查两个XML文件是否相等?

5 投票
3 回答
9526 浏览
提问于 2025-04-16 05:47

如何检查两个XML文件是否相同?

举个例子,即使两个XML文件的顺序不同,它们的内容也是一样的。我需要检查这两个XML文件是否包含相同的文字信息,而不考虑它们的顺序。

<a>
   <b>hello</b>
   <c><d>world</d></c>
</a>

<a>
   <c><d>world</d></c>
   <b>hello</b>
</a>

有没有什么工具可以做到这一点?

3 个回答

0

我的解决方案在下面。它比较了所有的属性和标签的迭代。部分代码参考自:测试xml.etree.ElementTree的等价性

import xml.etree.ElementTree as ET


def elements_equal(e1, e2):
    if e1.tag != e2.tag: 
        return False
    if e1.text != e2.text: 
        if  e1.text!=None and e2.text!=None :
            return False
    if e1.tail != e2.tail:
        if e1.tail!=None and e2.tail!=None:
            return False
    if e1.attrib != e2.attrib: 
        return False
    if len(e1) != len(e2): 
        return False
    return all(elements_equal(c1, c2) for c1, c2 in zip(e1, e2))



def is_two_xml_equal(f1, f2):
    tree1 = ET.parse(f1)
    root1 = tree1.getroot()
    tree2 = ET.parse(f2)
    root2 = tree2.getroot()
    return elements_equal(root1,root3)

f1 = '2.xml'
f2 = '1.xml'
print(is_two_xml_equal(f1, f2))
1

在XML中,顺序是很重要的,所以你提供的两个文件实际上是不同的。通常情况下,你可以先把XML标准化,然后直接把文件当作文本来比较。但是如果你想要不考虑顺序的比较,那你可能需要自己实现这个功能,使用市面上有很多的XML解析器(我推荐使用lxml)。

11

这完全取决于你对“等价”的定义。

假设你只关心文本节点(比如你例子中的 d 标签其实不重要,你只关心内容 word),你可以把每个文档的文本节点放到一个集合里,然后比较这些集合。使用 lxml 库的话,可以这样做:

from lxml import etree

tree1 = etree.parse('example1.xml')
tree2 = etree.parse('example2.xml')

print set(tree1.getroot().itertext()) == set(tree2.getroot().itertext())

你甚至可以选择忽略空白节点,做一些类似这样的操作:

set(i for i in tree.getroot().itertext() if i.strip())

需要注意的是,使用集合意味着你不会考虑某些文本在文档中出现的次数(这可能是你想要的,也可能不是)。如果顺序不重要,但出现的次数很重要,你可以用字典代替集合,记录每个文本出现的次数(比如用 collections.defaultdict()collections.Counter 在 Python 2.7 中)。

但是如果你只想忽略根元素的直接子元素的顺序(在你的例子中,就是 a 元素的子元素),而这些元素内部的内容是重要的,那你就需要另一种方法。你可以对每个子元素进行 XML 标准化,得到每个子元素的规范版本(不过,我不确定这是否足够符合你的需求)。

from lxml import etree

tree1 = etree.parse('example1.xml')
tree2 = etree.parse('example2.xml')

set1 = set(etree.tostring(i, method='c14n') for i in tree1.getroot())
set2 = set(etree.tostring(i, method='c14n') for i in tree2.getroot())

print set1 == set2

注意:为了让例子更简单,我使用了 lxml 的开发版本,在旧版本中,etree.tostring() 没有 method='c14n' 这个选项,只有 c14n() 方法在 ElementTree 上,它会写入一个类似文件的对象。所以在那种情况下,你需要把每个元素复制到自己的树中,并使用 StringIO() 对象作为一个虚拟文件。

另外,这种做法可能不适合处理非常大的文件。

但再次提醒:你真的需要清楚你所需要的“等价”是什么,并根据这个知识来创建你自己的解决方案!

撰写回答