Сравнение двух XML-файлов в Python
Я новичок в программировании на python, и у меня есть некоторые проблемы с пониманием концепции. Я хочу сравнить два XML-файла. Эти XML-файлы довольно большие. Я приведу пример для типа файлов, которые я хочу сравнить.
xmlfile1:
<xml>
<property1>
<property2>
<property3>
</property3>
</property2>
</property1>
</xml>
XML-файл2:
<xml>
<property1>
<property2>
<property3>
<property4>
</property4>
</property3>
</property2>
</property1>
</xml>
property1,property2, которые я назвал, отличаются от тех, которые на самом деле находятся в файле. В файле xml много свойств. И я хочу сравнить два XML-файла.
Я использую анализатор lxml, чтобы попытаться сравнить два файла и распечатать разницу между ними.
Я не знаю, как его разобрать и сравнить автоматически.
Я попытался прочитать через анализатор lxml, но я не мог понять, как использовать его для моей проблемы.
Может кто-нибудь, пожалуйста, скажите мне, как мне поступить с этой проблемой.
Фрагменты кода могут быть очень полезны
Еще один вопрос: я придерживаюсь правильной концепции или мне не хватает чего-то еще? Пожалуйста, исправьте меня в новых понятиях, о которых вы знаете
4 ответа
На самом деле это достаточно сложная проблема (из-за того, что "различие" означает часто находящийся здесь на виду у зрителя, поскольку будет семантически "эквивалентная" информация, которую вы, вероятно, не хотите отмечать как различия).
Вы можете попробовать использовать xmldiff, который основан на работе в Обнаружении изменений в иерархически структурированной информации.
Мой подход к проблеме заключался в преобразовании каждого XML в xml.etree.ElementTree и итерации по каждому из слоев. Я также включил функцию игнорирования списка атрибутов при выполнении сравнения.
Первый блок кода содержит используемый класс:
import xml.etree.ElementTree as ET
import logging
class XmlTree():
def __init__(self):
self.hdlr = logging.FileHandler('xml-comparison.log')
self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
@staticmethod
def convert_string_to_tree( xmlString):
return ET.fromstring(xmlString)
def xml_compare(self, x1, x2, excludes=[]):
"""
Compares two xml etrees
:param x1: the first tree
:param x2: the second tree
:param excludes: list of string of attributes to exclude from comparison
:return:
True if both files match
"""
if x1.tag != x2.tag:
self.logger.debug('Tags do not match: %s and %s' % (x1.tag, x2.tag))
return False
for name, value in x1.attrib.items():
if not name in excludes:
if x2.attrib.get(name) != value:
self.logger.debug('Attributes do not match: %s=%r, %s=%r'
% (name, value, name, x2.attrib.get(name)))
return False
for name in x2.attrib.keys():
if not name in excludes:
if name not in x1.attrib:
self.logger.debug('x2 has an attribute x1 is missing: %s'
% name)
return False
if not self.text_compare(x1.text, x2.text):
self.logger.debug('text: %r != %r' % (x1.text, x2.text))
return False
if not self.text_compare(x1.tail, x2.tail):
self.logger.debug('tail: %r != %r' % (x1.tail, x2.tail))
return False
cl1 = x1.getchildren()
cl2 = x2.getchildren()
if len(cl1) != len(cl2):
self.logger.debug('children length differs, %i != %i'
% (len(cl1), len(cl2)))
return False
i = 0
for c1, c2 in zip(cl1, cl2):
i += 1
if not c1.tag in excludes:
if not self.xml_compare(c1, c2, excludes):
self.logger.debug('children %i do not match: %s'
% (i, c1.tag))
return False
return True
def text_compare(self, t1, t2):
"""
Compare two text strings
:param t1: text one
:param t2: text two
:return:
True if a match
"""
if not t1 and not t2:
return True
if t1 == '*' or t2 == '*':
return True
return (t1 or '').strip() == (t2 or '').strip()
Второй блок кода содержит пару примеров XML и их сравнение:
xml1 = "<note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>"
xml2 = "<note><to>Tove</to><from>Daniel</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>"
tree1 = XmlTree.convert_string_to_tree(xml1)
tree2 = XmlTree.convert_string_to_tree(xml2)
comparator = XmlTree()
if comparator.xml_compare(tree1, tree2, ["from"]):
print "XMLs match"
else:
print "XMLs don't match"
Большая часть кредита для этого кода должна быть отдана syawar
Если вы намерены сравнить содержимое и атрибуты XML, а не просто сравнить файлы побайтно, в вопросе есть тонкости, поэтому нет решения, подходящего для всех случаев.
Вы должны знать кое-что о том, что важно в файлах XML.
Порядок атрибутов, перечисленных в теге элемента, обычно не имеет значения. То есть два XML-файла, которые различаются только порядком атрибутов элементов, обычно должны рассматриваться как одинаковые.
Но это общая часть.
Сложная часть зависит от приложения. Например, может случиться так, что форматирование некоторых элементов файла с использованием пробелов не имеет значения, а пробелы могут быть добавлены в XML для удобочитаемости. И так далее.
Последние версии
Я использовал эту функцию в модульных тестах недавнего проекта, чтобы сравнить известный вывод XML с выводом из пакета, который иногда меняет порядок атрибутов. В данном случае пробелы в текстовых элементах были неважны, но иногда использовались для форматирования.
import xml.etree.ElementTree as ET
def _canonicalize_XML( xml_str ):
""" Canonicalizes XML strings, so they are safe to
compare directly.
Strips white space from text content."""
if not hasattr( ET, "canonicalize" ):
raise Exception( "ElementTree missing canonicalize()" )
root = ET.fromstring( xml_str )
rootstr = ET.tostring( root )
return ET.canonicalize( rootstr, strip_text=True )
Чтобы использовать его, что-то вроде этого:
file1 = ET.parse('file1.xml')
file2 = ET.parse('file2.xml')
canon1 = _canonicalize_XML( ET.tostring( file1.getroot() ) )
canon2 = _canonicalize_XML( ET.tostring( file2.getroot() ) )
print( canon1 == canon2 )
В моем дистрибутиве Python 2 не имеет
Еще один скрипт, использующий xml.etree. Это ужасно, но это работает:)
#!/usr/bin/env python
import sys
import xml.etree.ElementTree as ET
from termcolor import colored
tree1 = ET.parse(sys.argv[1])
root1 = tree1.getroot()
tree2 = ET.parse(sys.argv[2])
root2 = tree2.getroot()
class Element:
def __init__(self,e):
self.name = e.tag
self.subs = {}
self.atts = {}
for child in e:
self.subs[child.tag] = Element(child)
for att in e.attrib.keys():
self.atts[att] = e.attrib[att]
print "name: %s, len(subs) = %d, len(atts) = %d" % ( self.name, len(self.subs), len(self.atts) )
def compare(self,el):
if self.name!=el.name:
raise RuntimeError("Two names are not the same")
print "----------------------------------------------------------------"
print self.name
print "----------------------------------------------------------------"
for att in self.atts.keys():
v1 = self.atts[att]
if att not in el.atts.keys():
v2 = '[NA]'
color = 'yellow'
else:
v2 = el.atts[att]
if v2==v1:
color = 'green'
else:
color = 'red'
print colored("first:\t%s = %s" % ( att, v1 ), color)
print colored("second:\t%s = %s" % ( att, v2 ), color)
for subName in self.subs.keys():
if subName not in el.subs.keys():
print colored("first:\thas got %s" % ( subName), 'purple')
print colored("second:\thasn't got %s" % ( subName), 'purple')
else:
self.subs[subName].compare( el.subs[subName] )
e1 = Element(root1)
e2 = Element(root2)
e1.compare(e2)