¿Cómo convertir una cadena xml a un diccionario?

125

Tengo un programa que lee un documento xml de un socket. Tengo el documento xml almacenado en una cadena que me gustaría convertir directamente a un diccionario Python, de la misma manera que se hace en la simplejsonbiblioteca de Django .

Toma como ejemplo:

str ="<?xml version="1.0" ?><person><name>john</name><age>20</age></person"
dic_xml = convert_to_dic(str)

Entonces dic_xmlse vería como{'person' : { 'name' : 'john', 'age' : 20 } }

usuario361526
fuente
str tiene algunos errores de sintaxis. try: str = '<? xml version = "1.0"?> <person> <name> john </name> <age> 20 </age> </person>'
Keir el

Respuestas:

58

Este es un gran módulo que alguien creó. Lo he usado varias veces. http://code.activestate.com/recipes/410469-xml-as-dictionary/

Aquí está el código del sitio web por si el enlace falla.

from xml.etree import cElementTree as ElementTree

class XmlListConfig(list):
    def __init__(self, aList):
        for element in aList:
            if element:
                # treat like dict
                if len(element) == 1 or element[0].tag != element[1].tag:
                    self.append(XmlDictConfig(element))
                # treat like list
                elif element[0].tag == element[1].tag:
                    self.append(XmlListConfig(element))
            elif element.text:
                text = element.text.strip()
                if text:
                    self.append(text)


class XmlDictConfig(dict):
    '''
    Example usage:

    >>> tree = ElementTree.parse('your_file.xml')
    >>> root = tree.getroot()
    >>> xmldict = XmlDictConfig(root)

    Or, if you want to use an XML string:

    >>> root = ElementTree.XML(xml_string)
    >>> xmldict = XmlDictConfig(root)

    And then use xmldict for what it is... a dict.
    '''
    def __init__(self, parent_element):
        if parent_element.items():
            self.update(dict(parent_element.items()))
        for element in parent_element:
            if element:
                # treat like dict - we assume that if the first two tags
                # in a series are different, then they are all different.
                if len(element) == 1 or element[0].tag != element[1].tag:
                    aDict = XmlDictConfig(element)
                # treat like list - we assume that if the first two tags
                # in a series are the same, then the rest are the same.
                else:
                    # here, we put the list in dictionary; the key is the
                    # tag name the list elements all share in common, and
                    # the value is the list itself 
                    aDict = {element[0].tag: XmlListConfig(element)}
                # if the tag has attributes, add those to the dict
                if element.items():
                    aDict.update(dict(element.items()))
                self.update({element.tag: aDict})
            # this assumes that if you've got an attribute in a tag,
            # you won't be having any text. This may or may not be a 
            # good idea -- time will tell. It works for the way we are
            # currently doing XML configuration files...
            elif element.items():
                self.update({element.tag: dict(element.items())})
            # finally, if there are no child tags and no attributes, extract
            # the text
            else:
                self.update({element.tag: element.text})

Ejemplo de uso:

tree = ElementTree.parse('your_file.xml')
root = tree.getroot()
xmldict = XmlDictConfig(root)

// O, si quieres usar una cadena XML:

root = ElementTree.XML(xml_string)
xmldict = XmlDictConfig(root)
James
fuente
44
U puede usar 'xmltodict' alternativamente
mrash
77
Intenté esto y es mucho más rápido que xmltodict. Para analizar un archivo xml de 80 MB, tardó 7 segundos, con el veredicto xml tardó 90 segundos
Eddy
1
Confirmado ... No he probado esto en todos los casos extremos, pero para mis cadenas XML bastante sencillas, esto es bastante rápido (aproximadamente 8 veces más rápido que la xmltodictbiblioteca). La desventaja es que debe alojarlo usted mismo dentro de su proyecto.
Dirk
10
Hola, esto funciona perfecto, agregará solo un fragmento para aquellos que no pueden encontrar cElementTree, simplemente cambie la primera línea a: from xml.etree import cElementTree as ElementTree
Rafael Aguilar
2
Voto negativo ya que hay mejores respuestas publicadas a continuación, particularmente en el manejo de múltiples etiquetas con el mismo nombre.
Maksym
280

xmltodict (divulgación completa: lo escribí) hace exactamente eso:

xmltodict.parse("""
<?xml version="1.0" ?>
<person>
  <name>john</name>
  <age>20</age>
</person>""")
# {u'person': {u'age': u'20', u'name': u'john'}}
Martin Blech
fuente
22
Este es un módulo fantástico.
zekel
2
me acabas de ahorrar mucho esfuerzo. Me alegró el día.
LRE
3
también, para futuros googlenauts: pude usar esto en App Engine, que me habían hecho creer que no funcionaba bien con la mayoría de las bibliotecas xml en Python.
LRE
2
La u solo indica que está almacenada una cadena Unicode. No afecta el valor de la cadena de ninguna manera.
Joshua Olson
2
Agradable. Y sí, @ypercube, hay una función xmldict.unparse () para el reverso.
Duther
47

El siguiente fragmento de XML-a-Python-dict analiza las entidades y los atributos que siguen esta "especificación" de XML a JSON . Es la solución más general que maneja todos los casos de XML.

from collections import defaultdict

def etree_to_dict(t):
    d = {t.tag: {} if t.attrib else None}
    children = list(t)
    if children:
        dd = defaultdict(list)
        for dc in map(etree_to_dict, children):
            for k, v in dc.items():
                dd[k].append(v)
        d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.items()}}
    if t.attrib:
        d[t.tag].update(('@' + k, v) for k, v in t.attrib.items())
    if t.text:
        text = t.text.strip()
        if children or t.attrib:
            if text:
              d[t.tag]['#text'] = text
        else:
            d[t.tag] = text
    return d

Esta usado:

from xml.etree import cElementTree as ET
e = ET.XML('''
<root>
  <e />
  <e>text</e>
  <e name="value" />
  <e name="value">text</e>
  <e> <a>text</a> <b>text</b> </e>
  <e> <a>text</a> <a>text</a> </e>
  <e> text <a>text</a> </e>
</root>
''')

from pprint import pprint
pprint(etree_to_dict(e))

El resultado de este ejemplo (según la "especificación" vinculada anteriormente) debería ser:

{'root': {'e': [None,
                'text',
                {'@name': 'value'},
                {'#text': 'text', '@name': 'value'},
                {'a': 'text', 'b': 'text'},
                {'a': ['text', 'text']},
                {'#text': 'text', 'a': 'text'}]}}

No es necesariamente bonito, pero no es ambiguo, y las entradas XML más simples dan como resultado un JSON más simple. :)


Actualizar

Si desea hacer lo contrario , emitir una cadena XML desde un JSON / dict , puede usar:

try:
  basestring
except NameError:  # python3
  basestring = str

def dict_to_etree(d):
    def _to_etree(d, root):
        if not d:
            pass
        elif isinstance(d, basestring):
            root.text = d
        elif isinstance(d, dict):
            for k,v in d.items():
                assert isinstance(k, basestring)
                if k.startswith('#'):
                    assert k == '#text' and isinstance(v, basestring)
                    root.text = v
                elif k.startswith('@'):
                    assert isinstance(v, basestring)
                    root.set(k[1:], v)
                elif isinstance(v, list):
                    for e in v:
                        _to_etree(e, ET.SubElement(root, k))
                else:
                    _to_etree(v, ET.SubElement(root, k))
        else:
            raise TypeError('invalid type: ' + str(type(d)))
    assert isinstance(d, dict) and len(d) == 1
    tag, body = next(iter(d.items()))
    node = ET.Element(tag)
    _to_etree(body, node)
    return ET.tostring(node)

pprint(dict_to_etree(d))
K3 --- rnc
fuente
1
Gracias por este código! Información adicional: si usa Python 2.5, no puede usar la comprensión del diccionario, por lo que debe cambiar la línea d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} a d = { t.tag: dict( (k, v[0] if len(v) == 1 else v) for k, v in dd.iteritems() ) }
M--
2
He probado casi 10 fragmentos / módulos de Python / etc. para eso. Este es el mejor que he encontrado. Según mis pruebas, es: 1) mucho más rápido que github.com/martinblech/xmltodict (basado en XML SAX api) 2) mejor que github.com/mcspring/XML2Dict que tiene algunos pequeños problemas cuando varios niños tienen los mismos nombres 3 ) mejor que code.activestate.com/recipes/410469-xml-as-dictionary que también tenía pequeños problemas y más importante: 4) ¡código mucho más corto que todos los anteriores! Gracias @ K3 --- rnc
Basj
Esta es, con mucho, la respuesta más completa, y funciona en> 2.6, y es bastante flexible. mi único problema es que el texto puede cambiar donde reside dependiendo de si hay un atributo o no). También publiqué una solución aún más pequeña y más rígida.
Erik Aronesty
1
Si necesita obtener una orden ordenada de un archivo XML, puede usar este mismo ejemplo con algunas modificaciones (consulte mi respuesta a continuación): stackoverflow.com/questions/2148119/…
serfer2
Esto también es bastante ingenioso y rápido cuando se usa con cElementTreeo lxml.etree. Tenga en cuenta que cuando se usa Python 3, todos .iteritems()deben cambiarse a .items()(mismo comportamiento pero la palabra clave cambió de Python 2 a 3).
Dirk
25

Esta versión ligera, aunque no es configurable, es bastante fácil de adaptar según sea necesario y funciona en pitones antiguos. También es rígido, lo que significa que los resultados son los mismos independientemente de la existencia de atributos.

import xml.etree.ElementTree as ET

from copy import copy

def dictify(r,root=True):
    if root:
        return {r.tag : dictify(r, False)}
    d=copy(r.attrib)
    if r.text:
        d["_text"]=r.text
    for x in r.findall("./*"):
        if x.tag not in d:
            d[x.tag]=[]
        d[x.tag].append(dictify(x,False))
    return d

Entonces:

root = ET.fromstring("<erik><a x='1'>v</a><a y='2'>w</a></erik>")

dictify(root)

Resultados en:

{'erik': {'a': [{'x': '1', '_text': 'v'}, {'y': '2', '_text': 'w'}]}}
Erik Aronesty
fuente
2
Me gusta esta solución Simple y no requiere libs externas.
MattK
6

Las versiones más recientes de las bibliotecas PicklingTools (1.3.0 y 1.3.1) admiten herramientas para convertir de XML a un dict de Python.

La descarga está disponible aquí: PicklingTools 1.3.1

Hay un poco de documentación de los convertidores aquí : la documentación se describen en detalle todas las decisiones y los problemas que surgirán cuando la conversión entre XML y diccionarios de Python (hay una serie de casos límite: atributos, listas, listas de anónimos, anónimo dicts, eval, etc. que la mayoría de los convertidores no manejan). Sin embargo, en general, los convertidores son fáciles de usar. Si un 'ejemplo.xml' contiene:

<top>
  <a>1</a>
  <b>2.2</b>
  <c>three</c>
</top>

Luego para convertirlo en un diccionario:

>>> from xmlloader import *
>>> example = file('example.xml', 'r')   # A document containing XML
>>> xl = StreamXMLLoader(example, 0)     # 0 = all defaults on operation
>>> result = xl.expect XML()
>>> print result
{'top': {'a': '1', 'c': 'three', 'b': '2.2'}}

Hay herramientas para convertir tanto en C ++ como en Python: C ++ y Python hacen conversión idéntica, pero C ++ es aproximadamente 60 veces más rápido

rts1
fuente
por supuesto, si hay 2 a's, este no es un buen formato.
Erik Aronesty
1
Parece interesante, pero aún no he descubierto cómo se deben usar las herramientas PicklingTools: ¿es solo un tarball de archivos de código fuente desde el que tengo que encontrar los correctos para mi trabajo y luego copiarlos en mi proyecto? ¿No hay módulos para cargar o algo más simple?
Dirk
Obtengo: en peekIntoNextNWSChar c = self.is .read (1) AttributeError: el objeto 'str' no tiene atributo 'read'
sqp_125
5

Puede hacerlo con bastante facilidad con lxml. Primero instálalo:

[sudo] pip install lxml

Aquí hay una función recursiva que escribí que hace el trabajo pesado por ti:

from lxml import objectify as xml_objectify


def xml_to_dict(xml_str):
    """ Convert xml to dict, using lxml v3.4.2 xml processing library """
    def xml_to_dict_recursion(xml_object):
        dict_object = xml_object.__dict__
        if not dict_object:
            return xml_object
        for key, value in dict_object.items():
            dict_object[key] = xml_to_dict_recursion(value)
        return dict_object
    return xml_to_dict_recursion(xml_objectify.fromstring(xml_str))

xml_string = """<?xml version="1.0" encoding="UTF-8"?><Response><NewOrderResp>
<IndustryType>Test</IndustryType><SomeData><SomeNestedData1>1234</SomeNestedData1>
<SomeNestedData2>3455</SomeNestedData2></SomeData></NewOrderResp></Response>"""

print xml_to_dict(xml_string)

La siguiente variante conserva la clave / elemento principal:

def xml_to_dict(xml_str):
    """ Convert xml to dict, using lxml v3.4.2 xml processing library, see http://lxml.de/ """
    def xml_to_dict_recursion(xml_object):
        dict_object = xml_object.__dict__
        if not dict_object:  # if empty dict returned
            return xml_object
        for key, value in dict_object.items():
            dict_object[key] = xml_to_dict_recursion(value)
        return dict_object
    xml_obj = objectify.fromstring(xml_str)
    return {xml_obj.tag: xml_to_dict_recursion(xml_obj)}

Si solo desea devolver un subárbol y convertirlo a dict, puede usar Element.find () para obtener el subárbol y luego convertirlo:

xml_obj.find('.//')  # lxml.objectify.ObjectifiedElement instance

Vea los documentos lxml aquí . ¡Espero que esto ayude!

radtek
fuente
5

Descargo de responsabilidad: este analizador XML modificado fue inspirado por Adam Clark. El analizador XML original funciona para la mayoría de los casos simples. Sin embargo, no funcionó para algunos archivos XML complicados. Depuré el código línea por línea y finalmente solucioné algunos problemas. Si encuentra algunos errores, hágamelo saber. Me alegra arreglarlo.

class XmlDictConfig(dict):  
    '''   
    Note: need to add a root into if no exising    
    Example usage:
    >>> tree = ElementTree.parse('your_file.xml')
    >>> root = tree.getroot()
    >>> xmldict = XmlDictConfig(root)
    Or, if you want to use an XML string:
    >>> root = ElementTree.XML(xml_string)
    >>> xmldict = XmlDictConfig(root)
    And then use xmldict for what it is... a dict.
    '''
    def __init__(self, parent_element):
        if parent_element.items():
            self.updateShim( dict(parent_element.items()) )
        for element in parent_element:
            if len(element):
                aDict = XmlDictConfig(element)
            #   if element.items():
            #   aDict.updateShim(dict(element.items()))
                self.updateShim({element.tag: aDict})
            elif element.items():    # items() is specialy for attribtes
                elementattrib= element.items()
                if element.text:           
                    elementattrib.append((element.tag,element.text ))     # add tag:text if there exist
                self.updateShim({element.tag: dict(elementattrib)})
            else:
                self.updateShim({element.tag: element.text})

    def updateShim (self, aDict ):
        for key in aDict.keys():   # keys() includes tag and attributes
            if key in self:
                value = self.pop(key)
                if type(value) is not list:
                    listOfDicts = []
                    listOfDicts.append(value)
                    listOfDicts.append(aDict[key])
                    self.update({key: listOfDicts})
                else:
                    value.append(aDict[key])
                    self.update({key: value})
            else:
                self.update({key:aDict[key]})  # it was self.update(aDict)    
Tigre
fuente
3
def xml_to_dict(node):
    u''' 
    @param node:lxml_node
    @return: dict 
    '''

    return {'tag': node.tag, 'text': node.text, 'attrib': node.attrib, 'children': {child.tag: xml_to_dict(child) for child in node}}
dibrovsd
fuente
2

El analizador XML más fácil de usar para Python es ElementTree (a partir de 2.5x y más arriba está en la biblioteca estándar xml.etree.ElementTree). No creo que haya nada que haga exactamente lo que quieres fuera de la caja. Sería bastante trivial escribir algo para hacer lo que quiera con ElementTree, pero por qué convertir a un diccionario y por qué no simplemente usar ElementTree directamente.


fuente
2

El código de http://code.activestate.com/recipes/410469-xml-as-dictionary/ funciona bien, pero si hay varios elementos que son iguales en un lugar determinado de la jerarquía, simplemente los anula.

Agregué una cuña entre esas miradas para ver si el elemento ya existe antes de self.update (). Si es así, muestra la entrada existente y crea una lista de lo existente y lo nuevo. Cualquier duplicado posterior se agrega a la lista.

No estoy seguro de si esto se puede manejar con más gracia, pero funciona:

import xml.etree.ElementTree as ElementTree

class XmlDictConfig(dict):
    def __init__(self, parent_element):
        if parent_element.items():
            self.updateShim(dict(parent_element.items()))
        for element in parent_element:
            if len(element):
                aDict = XmlDictConfig(element)
                if element.items():
                    aDict.updateShim(dict(element.items()))
                self.updateShim({element.tag: aDict})
            elif element.items():
                self.updateShim({element.tag: dict(element.items())})
            else:
                self.updateShim({element.tag: element.text.strip()})

    def updateShim (self, aDict ):
        for key in aDict.keys():
            if key in self:
                value = self.pop(key)
                if type(value) is not list:
                    listOfDicts = []
                    listOfDicts.append(value)
                    listOfDicts.append(aDict[key])
                    self.update({key: listOfDicts})

                else:
                    value.append(aDict[key])
                    self.update({key: value})
            else:
                self.update(aDict)
Adam Clark
fuente
2

De @ K3 --- respuesta rnc (lo mejor para mí) He agregado pequeñas modificaciones para obtener un OrderedDict de un texto XML (algunas veces el orden es importante):

def etree_to_ordereddict(t):
d = OrderedDict()
d[t.tag] = OrderedDict() if t.attrib else None
children = list(t)
if children:
    dd = OrderedDict()
    for dc in map(etree_to_ordereddict, children):
        for k, v in dc.iteritems():
            if k not in dd:
                dd[k] = list()
            dd[k].append(v)
    d = OrderedDict()
    d[t.tag] = OrderedDict()
    for k, v in dd.iteritems():
        if len(v) == 1:
            d[t.tag][k] = v[0]
        else:
            d[t.tag][k] = v
if t.attrib:
    d[t.tag].update(('@' + k, v) for k, v in t.attrib.iteritems())
if t.text:
    text = t.text.strip()
    if children or t.attrib:
        if text:
            d[t.tag]['#text'] = text
    else:
        d[t.tag] = text
return d

Siguiendo el ejemplo @ K3 --- rnc, puede usarlo:

from xml.etree import cElementTree as ET
e = ET.XML('''
<root>
  <e />
  <e>text</e>
  <e name="value" />
  <e name="value">text</e>
  <e> <a>text</a> <b>text</b> </e>
  <e> <a>text</a> <a>text</a> </e>
  <e> text <a>text</a> </e>
</root>
''')

from pprint import pprint
pprint(etree_to_ordereddict(e))

Espero eso ayude ;)

serfer2
fuente
1

Aquí hay un enlace a una solución ActiveState , y el código en caso de que desaparezca nuevamente.

==================================================
xmlreader.py:
==================================================
from xml.dom.minidom import parse


class NotTextNodeError:
    pass


def getTextFromNode(node):
    """
    scans through all children of node and gathers the
    text. if node has non-text child-nodes, then
    NotTextNodeError is raised.
    """
    t = ""
    for n in node.childNodes:
    if n.nodeType == n.TEXT_NODE:
        t += n.nodeValue
    else:
        raise NotTextNodeError
    return t


def nodeToDic(node):
    """
    nodeToDic() scans through the children of node and makes a
    dictionary from the content.
    three cases are differentiated:
    - if the node contains no other nodes, it is a text-node
    and {nodeName:text} is merged into the dictionary.
    - if the node has the attribute "method" set to "true",
    then it's children will be appended to a list and this
    list is merged to the dictionary in the form: {nodeName:list}.
    - else, nodeToDic() will call itself recursively on
    the nodes children (merging {nodeName:nodeToDic()} to
    the dictionary).
    """
    dic = {} 
    for n in node.childNodes:
    if n.nodeType != n.ELEMENT_NODE:
        continue
    if n.getAttribute("multiple") == "true":
        # node with multiple children:
        # put them in a list
        l = []
        for c in n.childNodes:
            if c.nodeType != n.ELEMENT_NODE:
            continue
        l.append(nodeToDic(c))
            dic.update({n.nodeName:l})
        continue

    try:
        text = getTextFromNode(n)
    except NotTextNodeError:
            # 'normal' node
            dic.update({n.nodeName:nodeToDic(n)})
            continue

        # text node
        dic.update({n.nodeName:text})
    continue
    return dic


def readConfig(filename):
    dom = parse(filename)
    return nodeToDic(dom)





def test():
    dic = readConfig("sample.xml")

    print dic["Config"]["Name"]
    print
    for item in dic["Config"]["Items"]:
    print "Item's Name:", item["Name"]
    print "Item's Value:", item["Value"]

test()



==================================================
sample.xml:
==================================================
<?xml version="1.0" encoding="UTF-8"?>

<Config>
    <Name>My Config File</Name>

    <Items multiple="true">
    <Item>
        <Name>First Item</Name>
        <Value>Value 1</Value>
    </Item>
    <Item>
        <Name>Second Item</Name>
        <Value>Value 2</Value>
    </Item>
    </Items>

</Config>



==================================================
output:
==================================================
My Config File

Item's Name: First Item
Item's Value: Value 1
Item's Name: Second Item
Item's Value: Value 2
tgray
fuente
Sí lo es. He reproducido el código aquí en caso de que vuelva a funcionar.
Jamie Bull
0

En un momento tuve que analizar y escribir XML que solo consistía en elementos sin atributos, por lo que fue posible una asignación 1: 1 de XML a dict fácilmente. Esto es lo que se me ocurrió en caso de que alguien más no necesite atributos:

def xmltodict(element):
    if not isinstance(element, ElementTree.Element):
        raise ValueError("must pass xml.etree.ElementTree.Element object")

    def xmltodict_handler(parent_element):
        result = dict()
        for element in parent_element:
            if len(element):
                obj = xmltodict_handler(element)
            else:
                obj = element.text

            if result.get(element.tag):
                if hasattr(result[element.tag], "append"):
                    result[element.tag].append(obj)
                else:
                    result[element.tag] = [result[element.tag], obj]
            else:
                result[element.tag] = obj
        return result

    return {element.tag: xmltodict_handler(element)}


def dicttoxml(element):
    if not isinstance(element, dict):
        raise ValueError("must pass dict type")
    if len(element) != 1:
        raise ValueError("dict must have exactly one root key")

    def dicttoxml_handler(result, key, value):
        if isinstance(value, list):
            for e in value:
                dicttoxml_handler(result, key, e)
        elif isinstance(value, basestring):
            elem = ElementTree.Element(key)
            elem.text = value
            result.append(elem)
        elif isinstance(value, int) or isinstance(value, float):
            elem = ElementTree.Element(key)
            elem.text = str(value)
            result.append(elem)
        elif value is None:
            result.append(ElementTree.Element(key))
        else:
            res = ElementTree.Element(key)
            for k, v in value.items():
                dicttoxml_handler(res, k, v)
            result.append(res)

    result = ElementTree.Element(element.keys()[0])
    for key, value in element[element.keys()[0]].items():
        dicttoxml_handler(result, key, value)
    return result

def xmlfiletodict(filename):
    return xmltodict(ElementTree.parse(filename).getroot())

def dicttoxmlfile(element, filename):
    ElementTree.ElementTree(dicttoxml(element)).write(filename)

def xmlstringtodict(xmlstring):
    return xmltodict(ElementTree.fromstring(xmlstring).getroot())

def dicttoxmlstring(element):
    return ElementTree.tostring(dicttoxml(element))
josch
fuente
0

@dibrovsd: la solución no funcionará si el xml tiene más de una etiqueta con el mismo nombre

En su línea de pensamiento, modifiqué un poco el código y lo escribí para el nodo general en lugar de root:

from collections import defaultdict
def xml2dict(node):
    d, count = defaultdict(list), 1
    for i in node:
        d[i.tag + "_" + str(count)]['text'] = i.findtext('.')[0]
        d[i.tag + "_" + str(count)]['attrib'] = i.attrib # attrib gives the list
        d[i.tag + "_" + str(count)]['children'] = xml2dict(i) # it gives dict
     return d
pg2455
fuente
0

He modificado una de las respuestas a mi gusto y para trabajar con varios valores con la misma etiqueta, por ejemplo, considere el siguiente código xml guardado en el archivo XML.xml

     <A>
        <B>
            <BB>inAB</BB>
            <C>
                <D>
                    <E>
                        inABCDE
                    </E>
                    <E>value2</E>
                    <E>value3</E>
                </D>
                <inCout-ofD>123</inCout-ofD>
            </C>
        </B>
        <B>abc</B>
        <F>F</F>
    </A>

y en python

import xml.etree.ElementTree as ET




class XMLToDictionary(dict):
    def __init__(self, parentElement):
        self.parentElement = parentElement
        for child in list(parentElement):
            child.text = child.text if (child.text != None) else  ' '
            if len(child) == 0:
                self.update(self._addToDict(key= child.tag, value = child.text.strip(), dict = self))
            else:
                innerChild = XMLToDictionary(parentElement=child)
                self.update(self._addToDict(key=innerChild.parentElement.tag, value=innerChild, dict=self))

    def getDict(self):
        return {self.parentElement.tag: self}

    class _addToDict(dict):
        def __init__(self, key, value, dict):
            if not key in dict:
                self.update({key: value})
            else:
                identical = dict[key] if type(dict[key]) == list else [dict[key]]
                self.update({key: identical + [value]})


tree = ET.parse('./XML.xml')
root = tree.getroot()
parseredDict = XMLToDictionary(root).getDict()
print(parseredDict)

la salida es

{'A': {'B': [{'BB': 'inAB', 'C': {'D': {'E': ['inABCDE', 'value2', 'value3']}, 'inCout-ofD': '123'}}, 'abc'], 'F': 'F'}}
descifrador
fuente
-2

Tengo un método recursivo para obtener un diccionario de un elemento lxml

    def recursive_dict(element):
        return (element.tag.split('}')[1],
                dict(map(recursive_dict, element.getchildren()),
                     **element.attrib))
moylop260
fuente
1
A esta solución le falta algún código, como importar y configurar. Recibí el mensaje 'str' objeto no tiene atributo 'etiqueta'
Chris Nielsen