Come convertire una stringa xml in un dizionario?


125

Ho un programma che legge un documento xml da un socket. Ho il documento xml memorizzato in una stringa che vorrei convertire direttamente in un dizionario Python, allo stesso modo in cui è fatto nella simplejsonlibreria di Django .

Prendi come esempio:

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

Quindi dic_xmlsarebbe simile{'person' : { 'name' : 'john', 'age' : 20 } }


str ha alcuni errori di sintassi. try: str = '<? xml version = "1.0"?> <person> <name> john </name> <age> 20 </age> </person>'
Keir

Risposte:


58

Questo è un ottimo modulo creato da qualcuno. L'ho usato più volte. http://code.activestate.com/recipes/410469-xml-as-dictionary/

Ecco il codice dal sito Web nel caso in cui il collegamento vada male.

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

Utilizzo di esempio:

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

// Oppure, se desideri utilizzare una stringa XML:

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

4
In alternativa puoi usare 'xmltodict'
mrash

7
Ho provato questo ed è molto più veloce di xmltodict. Per analizzare un file xml da 80 MB ci sono voluti 7 secondi, con xmltodict ci sono voluti 90 secondi
Eddy

1
Confermato ... Non l'ho testato su tutti i casi limite, ma per le mie stringhe XML piuttosto semplici, questo è abbastanza veloce (circa 8 volte più veloce della xmltodictlibreria). Lo svantaggio è che devi ospitarlo da solo all'interno del tuo progetto.
Dirk

10
Ciao, funziona perfettamente, aggiungerò solo un frammento per coloro che non riescono a trovare cElementTree, cambia la prima riga in: from xml.etree import cElementTree as ElementTree
Rafael Aguilar

2
Voto negativo poiché ci sono risposte migliori pubblicate di seguito, in particolare nella gestione di più tag con lo stesso nome.
Maksym

280

xmltodict (divulgazione completa: l'ho scritto) fa esattamente questo:

xmltodict.parse("""
<?xml version="1.0" ?>
<person>
  <name>john</name>
  <age>20</age>
</person>""")
# {u'person': {u'age': u'20', u'name': u'john'}}

22
Questo è un modulo fantastico.
zekel

2
mi hai appena risparmiato una grande quantità di sforzi. Ha reso la mia giornata.
LRE

3
inoltre, per i futuri googlenaut, sono stato in grado di usarlo in App Engine, che ero stato indotto a credere non funzionasse bene con la maggior parte delle librerie xml in Python.
LRE

2
La u sta solo indicando che è una stringa Unicode memorizzata. Non influisce in alcun modo sul valore della stringa.
Joshua Olson,

2
Bello. E sì, @ypercube, c'è una funzione xmldict.unparse () per il contrario.
Duther

47

Il seguente frammento da XML a Python-dict analizza le entità e gli attributi seguendo questa "specifica" da XML a JSON . È la soluzione più generale che gestisce tutti i casi di 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

È solito:

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

L'output di questo esempio (come da "specifica" collegata sopra) dovrebbe essere:

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

Non necessariamente carino, ma non è ambiguo e gli input XML più semplici risultano in JSON più semplice. :)


Aggiornare

Se vuoi fare il contrario , emetti una stringa XML da un JSON / dict , puoi usare:

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

1
Grazie per questo codice! Informazioni aggiuntive: se usi python 2.5 non puoi usare la comprensione del dizionario, quindi devi cambiare la riga d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} in d = { t.tag: dict( (k, v[0] if len(v) == 1 else v) for k, v in dd.iteritems() ) }
M--

2
Ho testato quasi 10 snippet / moduli python / ecc. Per questo. Questo è il migliore che ho trovato. Secondo i miei test, è: 1) molto più veloce di github.com/martinblech/xmltodict (basato su XML SAX api) 2) migliore di github.com/mcspring/XML2Dict che ha alcuni piccoli problemi quando diversi bambini hanno lo stesso nome 3 ) meglio di code.activestate.com/recipes/410469-xml-as-dictionary che aveva anche piccoli problemi e più importanti: 4) codice molto più breve di tutti i precedenti! Grazie @ K3 --- rnc
Basj

Questa è di gran lunga la risposta più completa, funziona con> 2.6 ed è abbastanza flessibile. il mio unico problema è che il testo può cambiare dove risiede a seconda che ci sia o meno un attributo). ho pubblicato anche una soluzione ancora più piccola e più rigida.
Erik Aronesty

1
Se avete bisogno di ottenere un dict ordinata da un file XML, per favore, è possibile utilizzare questo stesso esempio con poche modifiche (vedi la mia risposta qui sotto): stackoverflow.com/questions/2148119/...
serfer2

Questo è anche abbastanza elegante e veloce se usato con cElementTreeo lxml.etree. Nota che quando usi Python 3, tutti .iteritems()devono essere modificati in .items()(stesso comportamento ma la parola chiave è cambiata da Python 2 a 3).
Dirk

25

Questa versione leggera, sebbene non configurabile, è abbastanza facile da adattare secondo necessità e funziona con i vecchi pitoni. Inoltre è rigido, il che significa che i risultati sono gli stessi indipendentemente dall'esistenza degli attributi.

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

Così:

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

dictify(root)

Risultati in:

{'erik': {'a': [{'x': '1', '_text': 'v'}, {'y': '2', '_text': 'w'}]}}

2
Mi piace questa soluzione. Semplice e non richiede librerie esterne.
MattK

6

Le versioni più recenti delle librerie PicklingTools (1.3.0 e 1.3.1) supportano gli strumenti per la conversione da XML a un dict Python.

Il download è disponibile qui: PicklingTools 1.3.1

C'è un bel po 'di documentazione per i convertitori qui : la documentazione descrive in dettaglio tutte le decisioni e i problemi che sorgeranno durante la conversione tra dizionari XML e Python (ci sono una serie di casi limite: attributi, elenchi, elenchi anonimi, elenchi anonimi dicts, eval, ecc. che la maggior parte dei convertitori non gestisce). In generale, però, i convertitori sono facili da usare. Se un "esempio.xml" contiene:

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

Quindi per convertirlo in un dizionario:

>>> 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'}}

Esistono strumenti per la conversione sia in C ++ che in Python: C ++ e Python eseguono la conversione indentical, ma C ++ è circa 60 volte più veloce


ovviamente, se ci sono 2 a, questo non è un buon formato.
Erik Aronesty

1
Sembra interessante, ma non ho ancora capito come devono essere utilizzati i PicklingTools - è solo un archivio di file di codice sorgente da cui devo trovare quelli giusti per il mio lavoro e poi copiarli nel mio progetto? Nessun modulo da caricare o qualcosa di più semplice?
Dirk

Ottengo: in peekIntoNextNWSChar c = self.is .read (1) AttributeError: l'oggetto 'str' non ha attributo 'read'
sqp_125

5

Puoi farlo abbastanza facilmente con lxml. Prima installalo:

[sudo] pip install lxml

Ecco una funzione ricorsiva che ho scritto che fa il lavoro pesante per te:

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 variante seguente conserva la chiave / elemento genitore:

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

Se vuoi restituire solo un sottoalbero e convertirlo in dict, puoi usare Element.find () per ottenere il sottoalbero e poi convertirlo:

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

Vedi i documenti lxml qui . Spero che aiuti!


5

Dichiarazione di non responsabilità: questo parser XML modificato è stato ispirato da Adam Clark Il parser XML originale funziona per la maggior parte dei casi semplici. Tuttavia, non ha funzionato per alcuni file XML complicati. Ho eseguito il debug del codice riga per riga e alla fine ho risolto alcuni problemi. Se trovi qualche bug, fammelo sapere. Sono felice di risolverlo.

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)    

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

2

Il parser XML più facile da usare per Python è ElementTree (a partire da 2.5x e versioni successive si trova nella libreria standard xml.etree.ElementTree). Non penso che ci sia qualcosa che fa esattamente quello che vuoi fuori dagli schemi. Sarebbe piuttosto banale scrivere qualcosa per fare quello che vuoi usando ElementTree, ma perché convertirlo in un dizionario e perché non usare direttamente ElementTree.


2

Il codice da http://code.activestate.com/recipes/410469-xml-as-dictionary/ funziona bene, ma se ci sono più elementi uguali in un dato punto della gerarchia, li sovrascrive.

Ho aggiunto uno spessore tra quello sembra per vedere se l'elemento esiste già prima di self.update (). In tal caso, visualizza la voce esistente e crea un elenco tra quella esistente e quella nuova. Eventuali duplicati successivi vengono aggiunti all'elenco.

Non sono sicuro che questo possa essere gestito in modo più grazioso, ma funziona:

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)

2

Da @ K3 --- risposta rnc (la migliore per me) ho aggiunto piccole modifiche per ottenere un OrderedDict da un testo XML (a volte l'ordine è 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

Seguendo l'esempio @ K3 --- rnc, puoi 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))

Spero che sia d'aiuto ;)


1

Ecco un collegamento a una soluzione ActiveState e il codice nel caso in cui scompaia di nuovo.

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

Sì. Ho riprodotto il codice qui nel caso in cui vada di nuovo.
Jamie Bull

0

A un certo punto ho dovuto analizzare e scrivere XML che consisteva solo di elementi senza attributi, quindi una mappatura 1: 1 da XML a dict era facilmente possibile. Questo è ciò che mi è venuto in mente nel caso in cui qualcun altro non abbia bisogno di attributi:

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

0

@dibrovsd: la soluzione non funzionerà se l'xml ha più di un tag con lo stesso nome

Sulla tua linea di pensiero, ho modificato un po 'il codice e l'ho scritto per il nodo generale invece che per 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

0

Ho modificato una delle risposte a mio gusto e per lavorare con più valori con lo stesso tag, ad esempio, considera il seguente codice xml salvato nel file 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>

e in pitone

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)

l'uscita è

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

-2

Ho un metodo ricorsivo per ottenere un dizionario da un elemento lxml

    def recursive_dict(element):
        return (element.tag.split('}')[1],
                dict(map(recursive_dict, element.getchildren()),
                     **element.attrib))

1
In questa soluzione manca del codice, come importazione e configurazione. Ho ricevuto il messaggio "str" ​​L'oggetto non ha attributo "tag"
Chris Nielsen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.