比较XML片段?

Gre*_*ind 33 python xml diff

在另一个SO问题的基础上,如何检查两个结构良好的XML片段在语义上是否相等.我需要的只是"平等"与否,因为我正在使用它进行单元测试.

在我想要的系统中,这些是相同的(注意'start'和'end'的顺序):

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<Stats start="1275955200" end="1276041599">
</Stats>

# Reordered start and end

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<Stats end="1276041599" start="1275955200" >
</Stats>
Run Code Online (Sandbox Code Playgroud)

我有lmxl和其他工具供我使用,一个只允许重新排序属性的简单功能也可以正常工作!


基于IanB答案的工作片段:

from formencode.doctest_xml_compare import xml_compare
# have to strip these or fromstring carps
xml1 = """    <?xml version='1.0' encoding='utf-8' standalone='yes'?>
    <Stats start="1275955200" end="1276041599"></Stats>"""
xml2 = """     <?xml version='1.0' encoding='utf-8' standalone='yes'?>
    <Stats end="1276041599" start="1275955200"></Stats>"""
xml3 = """ <?xml version='1.0' encoding='utf-8' standalone='yes'?>
    <Stats start="1275955200"></Stats>"""

from lxml import etree
tree1 = etree.fromstring(xml1.strip())
tree2 = etree.fromstring(xml2.strip())
tree3 = etree.fromstring(xml3.strip())

import sys
reporter = lambda x: sys.stdout.write(x + "\n")

assert xml_compare(tree1,tree2,reporter)
assert xml_compare(tree1,tree3,reporter) is False
Run Code Online (Sandbox Code Playgroud)

Ian*_*ing 27

您可以使用formencode.doctest_xml_compare - xml_compare函数比较两个ElementTree或lxml树.

  • 此函数不正确,如果在xml中交换属性顺序,它将返回False. (2认同)

Ane*_*pic 14

元素的顺序在XML中可能是重要的,这可能是为什么大多数其他方法建议将比较不相等,如果顺序不同...即使元素具有相同的属性和文本内容.

但我也想要一个对顺序不敏感的比较,所以我想出了这个:

from lxml import etree
import xmltodict  # pip install xmltodict


def normalise_dict(d):
    """
    Recursively convert dict-like object (eg OrderedDict) into plain dict.
    Sorts list values.
    """
    out = {}
    for k, v in dict(d).iteritems():
        if hasattr(v, 'iteritems'):
            out[k] = normalise_dict(v)
        elif isinstance(v, list):
            out[k] = []
            for item in sorted(v):
                if hasattr(item, 'iteritems'):
                    out[k].append(normalise_dict(item))
                else:
                    out[k].append(item)
        else:
            out[k] = v
    return out


def xml_compare(a, b):
    """
    Compares two XML documents (as string or etree)

    Does not care about element order
    """
    if not isinstance(a, basestring):
        a = etree.tostring(a)
    if not isinstance(b, basestring):
        b = etree.tostring(b)
    a = normalise_dict(xmltodict.parse(a))
    b = normalise_dict(xmltodict.parse(b))
    return a == b
Run Code Online (Sandbox Code Playgroud)

  • 有两件事需要考虑:_attributes_的顺序无关紧要.但是,元素的顺序在XML中很重要,此代码适用于您不关心元素顺序的特殊情况 (4认同)

Mar*_*ase 5

我有同样的问题:我想要比较的两个文件具有相同的属性,但顺序不同.

似乎lxml中的XML Canonicalization(C14N)适用于此,但我绝对不是XML专家.我很想知道其他人是否可以指出这种方法的缺点.

parser = etree.XMLParser(remove_blank_text=True)

xml1 = etree.fromstring(xml_string1, parser)
xml2 = etree.fromstring(xml_string2, parser)

print "xml1 == xml2: " + str(xml1 == xml2)

ppxml1 = etree.tostring(xml1, pretty_print=True)
ppxml2 = etree.tostring(xml2, pretty_print=True)

print "pretty(xml1) == pretty(xml2): " + str(ppxml1 == ppxml2)

xml_string_io1 = StringIO()
xml1.getroottree().write_c14n(xml_string_io1)
cxml1 = xml_string_io1.getvalue()

xml_string_io2 = StringIO()
xml2.getroottree().write_c14n(xml_string_io2)
cxml2 = xml_string_io2.getvalue()

print "canonicalize(xml1) == canonicalize(xml2): " + str(cxml1 == cxml2)
Run Code Online (Sandbox Code Playgroud)

运行这个给了我:

$ python test.py 
xml1 == xml2: false
pretty(xml1) == pretty(xml2): false
canonicalize(xml1) == canonicalize(xml2): true
Run Code Online (Sandbox Code Playgroud)


Gui*_*ent 5

这是一个简单的解决方案,将XML转换为字典(使用xmltodict)并将字典比较在一起

import json
import xmltodict

class XmlDiff(object):
    def __init__(self, xml1, xml2):
        self.dict1 = json.loads(json.dumps((xmltodict.parse(xml1))))
        self.dict2 = json.loads(json.dumps((xmltodict.parse(xml2))))

    def equal(self):
        return self.dict1 == self.dict2
Run Code Online (Sandbox Code Playgroud)

单元测试

import unittest

class XMLDiffTestCase(unittest.TestCase):

    def test_xml_equal(self):
        xml1 = """<?xml version='1.0' encoding='utf-8' standalone='yes'?>
        <Stats start="1275955200" end="1276041599">
        </Stats>"""
        xml2 = """<?xml version='1.0' encoding='utf-8' standalone='yes'?>
        <Stats end="1276041599" start="1275955200" >
        </Stats>"""
        self.assertTrue(XmlDiff(xml1, xml2).equal())

    def test_xml_not_equal(self):
        xml1 = """<?xml version='1.0' encoding='utf-8' standalone='yes'?>
        <Stats start="1275955200">
        </Stats>"""
        xml2 = """<?xml version='1.0' encoding='utf-8' standalone='yes'?>
        <Stats end="1276041599" start="1275955200" >
        </Stats>"""
        self.assertFalse(XmlDiff(xml1, xml2).equal())
Run Code Online (Sandbox Code Playgroud)

或者在简单的python方法中:

import json
import xmltodict

def xml_equal(a, b):
    """
    Compares two XML documents (as string or etree)

    Does not care about element order
    """
    return json.loads(json.dumps((xmltodict.parse(a)))) == json.loads(json.dumps((xmltodict.parse(b))))
Run Code Online (Sandbox Code Playgroud)