phone number machine learning classifying classify python xml diff

python - machine - nltk phone number



¿Comparar fragmentos de XML? (10)

¿Qué pasa con el siguiente fragmento de código? Se puede mejorar fácilmente para incluir attribs también:

def separator(self): return "!@#$%^&*" # Very ugly separator def _traverseXML(self, xmlElem, tags, xpaths): tags.append(xmlElem.tag) for e in xmlElem: self._traverseXML(e, tags, xpaths) text = '''' if (xmlElem.text): text = xmlElem.text.strip() xpaths.add("/".join(tags) + self.separator() + text) tags.pop() def _xmlToSet(self, xml): xpaths = set() # output tags = list() root = ET.fromstring(xml) self._traverseXML(root, tags, xpaths) return xpaths def _areXMLsAlike(self, xml1, xml2): xpaths1 = self._xmlToSet(xml1) xpaths2 = self._xmlToSet(xml2)`enter code here` return xpaths1 == xpaths2

Sobre la base de otra pregunta de SO , ¿cómo se puede verificar si dos fragmentos XML bien formados son semánticamente iguales? Todo lo que necesito es "igual" o no, ya que estoy usando esto para pruebas unitarias.

En el sistema que quiero, estos serían iguales (tenga en cuenta el orden de ''inicio'' y ''final''):

<?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>

Tengo lmxl y otras herramientas a mi disposición, ¡y una función simple que solo permite el reordenamiento de atributos también funcionaría bien!

Fragmento de trabajo basado en la respuesta de 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


Adaptando share a Python 3 (básicamente, cambia iteritems() a items() y basestring a string ):

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).items(): 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, str): a = etree.tostring(a) if not isinstance(b, str): b = etree.tostring(b) a = normalise_dict(xmltodict.parse(a)) b = normalise_dict(xmltodict.parse(b)) return a == b


Al pensar en este problema, se me ocurrió la siguiente solución que hace que los elementos XML sean comparables y clasificables:

import xml.etree.ElementTree as ET def cmpElement(x, y): # compare type r = cmp(type(x), type(y)) if r: return r # compare tag r = cmp(x.tag, y.tag) if r: return r # compare tag attributes r = cmp(x.attrib, y.attrib) if r: return r # compare stripped text content xtext = (x.text and x.text.strip()) or None ytext = (y.text and y.text.strip()) or None r = cmp(xtext, ytext) if r: return r # compare sorted children if len(x) or len(y): return cmp(sorted(x.getchildren()), sorted(y.getchildren())) return 0 ET._ElementInterface.__lt__ = lambda self, other: cmpElement(self, other) == -1 ET._ElementInterface.__gt__ = lambda self, other: cmpElement(self, other) == 1 ET._ElementInterface.__le__ = lambda self, other: cmpElement(self, other) <= 0 ET._ElementInterface.__ge__ = lambda self, other: cmpElement(self, other) >= 0 ET._ElementInterface.__eq__ = lambda self, other: cmpElement(self, other) == 0 ET._ElementInterface.__ne__ = lambda self, other: cmpElement(self, other) != 0


Aquí una solución simple, convierta XML en diccionarios (con xmltodict ) y compare diccionarios juntos

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

prueba de unidad

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())

o en el método simple de 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))))


Dado que el orden de los atributos no es significativo en XML , quiere ignorar las diferencias debido a los diferentes ordenamientos de atributos y la canonicalización de XML (C14N) ordena los atributos de manera determinista, puede usar ese método para probar la igualdad:

xml1 = b'''''' <?xml version=''1.0'' encoding=''utf-8'' standalone=''yes''?> <Stats start="1275955200" end="1276041599"></Stats>'''''' xml2 = b'''''' <?xml version=''1.0'' encoding=''utf-8'' standalone=''yes''?> <Stats end="1276041599" start="1275955200"></Stats>'''''' xml3 = b'''''' <?xml version=''1.0'' encoding=''utf-8'' standalone=''yes''?> <Stats start="1275955200"></Stats>'''''' import lxml.etree tree1 = lxml.etree.fromstring(xml1.strip()) tree2 = lxml.etree.fromstring(xml2.strip()) tree3 = lxml.etree.fromstring(xml3.strip()) import io b1 = io.BytesIO() b2 = io.BytesIO() b3 = io.BytesIO() tree1.getroottree().write_c14n(b1) tree2.getroottree().write_c14n(b2) tree3.getroottree().write_c14n(b3) assert b1.getvalue() == b2.getvalue() assert b1.getvalue() != b3.getvalue()

Tenga en cuenta que este ejemplo supone Python 3. Con Python 3, el uso de b''''''...'''''' cadenas y io.BytesIO es obligatorio, mientras que con Python 2 este método también funciona con cadenas normales y io.StringIO .


El orden de los elementos puede ser significativo en XML, esta puede ser la razón por la cual la mayoría de los otros métodos sugeridos se compararán desiguales si el orden es diferente ... incluso si los elementos tienen los mismos atributos y contenido de texto.

Pero también quería una comparación insensible al orden, así que se me ocurrió esto:

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



Si toma un enfoque DOM, puede recorrer los dos árboles simultáneamente mientras compara los nodos (tipo de nodo, texto, atributos) sobre la marcha.

Una solución recursiva será la más elegante: solo una comparación adicional en cortocircuito una vez que un par de nodos no son "iguales" o una vez que detecta una hoja en un árbol cuando se trata de una rama en otro, etc.



Tuve el mismo problema: dos documentos que quería comparar que tenían los mismos atributos pero en diferentes órdenes.

Parece que XML Canonicalization (C14N) en lxml funciona bien para esto, pero definitivamente no soy un experto en XML. Tengo curiosidad por saber si alguien más puede señalar inconvenientes a este enfoque.

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)

Ejecutar esto me da:

$ python test.py xml1 == xml2: false pretty(xml1) == pretty(xml2): false canonicalize(xml1) == canonicalize(xml2): true