Wie konvertiere ich eine XML-Zeichenfolge in ein Wörterbuch?


125

Ich habe ein Programm, das ein XML-Dokument aus einem Socket liest. Ich habe das XML-Dokument in einer Zeichenfolge gespeichert, die ich direkt in ein Python-Wörterbuch konvertieren möchte, genauso wie es in Djangos simplejsonBibliothek gemacht wird.

Nehmen Sie als Beispiel:

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

Dann dic_xmlwürde es so aussehen{'person' : { 'name' : 'john', 'age' : 20 } }


str hat einige Syntaxfehler. try: str = '<? xml version = "1.0"?> <person> <name> john </ name> <age> 20 </ age> </ person>'
Keir

Antworten:


58

Dies ist ein großartiges Modul, das jemand erstellt hat. Ich habe es mehrmals benutzt. http://code.activestate.com/recipes/410469-xml-as-dictionary/

Hier ist der Code von der Website für den Fall, dass der Link schlecht wird.

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

Anwendungsbeispiel:

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

// Oder wenn Sie eine XML-Zeichenfolge verwenden möchten:

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

4
Sie können alternativ 'xmltodict' verwenden
mrash

7
Ich habe es versucht und es ist viel schneller als xmltodict. Das Parsen einer 80-MB-XML-Datei dauerte 7 Sekunden, mit xmltodict 90 Sekunden
Eddy

1
Bestätigt ... Ich habe dies nicht für jeden Randfall getestet, aber für meine eher unkomplizierten XML-Zeichenfolgen ist dies ziemlich schnell (ungefähr 8-mal schneller als die xmltodictBibliothek). Nachteil ist, dass Sie es selbst in Ihrem Projekt hosten müssen.
Dirk

10
Hallo, das funktioniert perfekt. Es wird nur ein Ausschnitt für diejenigen hinzugefügt, die es nicht finden cElementTreekönnen. Ändern Sie einfach die erste Zeile in: from xml.etree import cElementTree as ElementTree
Rafael Aguilar

2
Down-Voting, da es unten bessere Antworten gibt, insbesondere beim Umgang mit mehreren Tags mit demselben Namen.
Maksym

279

xmltodict (vollständige Offenlegung: Ich habe es geschrieben) macht genau das:

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

21
Dies ist ein fantastisches Modul.
Zekel

2
Sie haben mir gerade viel Mühe erspart. Meinen Tag gerettet.
LRE

3
auch für zukünftige googlenauts - ich konnte dies in App Engine verwenden, was meiner Meinung nach mit den meisten XML-Bibliotheken in Python nicht gut funktioniert hat.
LRE

2
Das u zeigt nur an, dass es sich um eine gespeicherte Unicode-Zeichenfolge handelt. Der Wert der Zeichenfolge wird in keiner Weise beeinflusst.
Joshua Olson

2
Nett. Und ja, @ypercube, es gibt eine xmldict.unparse () -Funktion für die Umkehrung.
Duther

47

Das folgende XML-zu-Python-diktierte Snippet analysiert Entitäten sowie Attribute, die dieser XML-zu-JSON- "Spezifikation" folgen . Es ist die allgemeinste Lösung für alle Fälle von 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

Es ist benutzt:

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

Die Ausgabe dieses Beispiels (gemäß der oben verlinkten "Spezifikation") sollte sein:

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

Nicht unbedingt hübsch, aber eindeutig, und einfachere XML-Eingaben führen zu einfacherem JSON. :) :)


Aktualisieren

Wenn Sie das Gegenteil tun möchten , geben Sie eine XML-Zeichenfolge aus einem JSON / dict aus . Sie können Folgendes verwenden:

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
Danke für diesen Code! Zusätzliche Informationen: Wenn Sie Python 2.5 verwenden, können Sie das Wörterbuchverständnis nicht verwenden, daher müssen Sie die Zeile 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
Ich habe fast 10 Snippets / Python-Module / etc. dafür getestet. Dieser ist der beste, den ich gefunden habe. Nach meinen Tests ist es: 1) viel schneller als github.com/martinblech/xmltodict (basierend auf XML SAX api) 2) besser als github.com/mcspring/XML2Dict, was einige kleine Probleme hat, wenn mehrere Kinder den gleichen Namen haben 3 ) besser als code.activestate.com/recipes/410469-xml-as-dictionary, das ebenfalls kleine Probleme hatte und wichtiger: 4) viel kürzerer Code als alle vorherigen! Danke @ K3 --- rnc
Basj

Dies ist bei weitem die umfassendste Antwort, und sie funktioniert mit> 2.6 und ist ziemlich flexibel. Mein einziges Problem ist, dass sich der Text dort ändern kann, wo er sich befindet, je nachdem, ob es ein Attribut gibt oder nicht. Ich habe auch eine noch kleinere und starrere Lösung veröffentlicht.
Erik Aronesty

1
Wenn Sie ein geordnetes Diktat aus einer XML-Datei benötigen, können Sie dasselbe Beispiel mit wenigen Änderungen verwenden (siehe meine Antwort unten): stackoverflow.com/questions/2148119/…
serfer2

Dies ist auch ziemlich geschickt und schnell, wenn es mit cElementTreeoder verwendet wird lxml.etree. Beachten Sie, dass bei Verwendung von Python 3 alle .iteritems()geändert werden müssen .items()(dasselbe Verhalten, aber das Schlüsselwort wurde von Python 2 in 3 geändert).
Dirk

25

Diese leichtgewichtige Version ist zwar nicht konfigurierbar, lässt sich jedoch nach Bedarf leicht anpassen und funktioniert in alten Pythons. Es ist auch starr - was bedeutet, dass die Ergebnisse unabhängig von der Existenz von Attributen gleich sind.

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

So:

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

dictify(root)

Ergebnisse in:

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

2
Ich mag diese Lösung. Einfach und erfordert keine externen Bibliotheken.
MattK

6

Die neuesten Versionen der PicklingTools-Bibliotheken (1.3.0 und 1.3.1) unterstützen Tools zum Konvertieren von XML in ein Python-Diktat.

Der Download ist hier verfügbar: PicklingTools 1.3.1

Es gibt einiges an Dokumentation für die Wandler hier : Die Dokumentation beschreibt ausführlich alle Entscheidungen und Probleme , die auftreten, wenn zwischen XML und Python Wörterbücher (es gibt eine Reihe von Rand Fällen Umwandlung: Attribute, Listen, anonyme Listen, anonym Diktate, Auswertungen usw., die die meisten Konverter nicht verarbeiten). Im Allgemeinen sind die Konverter jedoch einfach zu bedienen. Wenn eine 'example.xml' enthält:

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

Um es dann in ein Wörterbuch umzuwandeln:

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

Es gibt Tools zum Konvertieren in C ++ und Python: C ++ und Python konvertieren indentisch, aber C ++ ist etwa 60-mal schneller


Wenn es dann 2 A gibt, ist dies natürlich kein gutes Format.
Erik Aronesty

1
Sieht interessant aus, aber ich habe noch nicht herausgefunden, wie die PicklingTools verwendet werden sollen. Ist dies nur eine Zusammenstellung von Quellcodedateien, aus denen ich die richtigen für meinen Job finden und sie dann in mein Projekt kopieren muss? Keine Module zum Laden oder etwas einfacher?
Dirk

Ich erhalte: in peekIntoNextNWSChar c = self.is .read (1) AttributeError: 'str'-Objekt hat kein Attribut' read '
sqp_125

5

Sie können dies ganz einfach mit lxml tun. Installieren Sie es zuerst:

[sudo] pip install lxml

Hier ist eine rekursive Funktion, die ich geschrieben habe und die das schwere Heben für Sie erledigt:

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)

Die folgende Variante behält den übergeordneten Schlüssel / das übergeordnete Element bei:

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

Wenn Sie nur einen Teilbaum zurückgeben und in Dikt konvertieren möchten, können Sie Element.find () verwenden , um den Teilbaum abzurufen und ihn dann zu konvertieren:

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

Siehe die lxml-Dokumente hier . Ich hoffe das hilft!


5

Haftungsausschluss: Dieser modifizierte XML-Parser wurde von Adam Clark inspiriert. Der ursprüngliche XML-Parser funktioniert in den meisten einfachen Fällen. Bei einigen komplizierten XML-Dateien funktionierte dies jedoch nicht. Ich habe den Code Zeile für Zeile getestet und schließlich einige Probleme behoben. Wenn Sie Fehler finden, lassen Sie es mich bitte wissen. Ich bin froh, das Problem zu beheben.

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

Der am einfachsten zu verwendende XML-Parser für Python ist ElementTree (ab 2.5x in der Standardbibliothek xml.etree.ElementTree). Ich glaube nicht, dass irgendetwas genau das tut, was Sie wollen. Es wäre ziemlich trivial, mit ElementTree etwas zu schreiben, um das zu tun, was Sie wollen, aber warum in ein Wörterbuch konvertieren und warum nicht einfach ElementTree direkt verwenden.


2

Der Code von http://code.activestate.com/recipes/410469-xml-as-dictionary/ funktioniert gut, aber wenn es mehrere Elemente gibt, die an einer bestimmten Stelle in der Hierarchie gleich sind, werden sie nur überschrieben.

Ich habe einen Shim dazwischen hinzugefügt, um zu sehen, ob das Element bereits vor self.update () vorhanden ist. Wenn ja, wird der vorhandene Eintrag eingeblendet und eine Liste aus dem vorhandenen und dem neuen erstellt. Alle nachfolgenden Duplikate werden der Liste hinzugefügt.

Ich bin mir nicht sicher, ob dies eleganter gehandhabt werden kann, aber es funktioniert:

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

Von @ K3 --- rnc Antwort (das Beste für mich) Ich habe eine kleine Änderung hinzugefügt, um ein OrderedDict aus einem XML-Text zu erhalten (manchmal ist die Reihenfolge wichtig):

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

Nach dem Beispiel von @ K3 --- rnc können Sie es verwenden:

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

Ich hoffe es hilft ;)


1

Hier ist ein Link zu einer ActiveState-Lösung - und der Code für den Fall, dass er wieder verschwindet.

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

Ja, so ist es. Habe den Code hier reproduziert, falls er wieder geht.
Jamie Bull

0

Irgendwann musste ich XML analysieren und schreiben, das nur aus Elementen ohne Attribute bestand, sodass eine 1: 1-Zuordnung von XML zu Diktat problemlos möglich war. Dies ist, was ich mir ausgedacht habe, falls jemand anderes auch keine Attribute benötigt:

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: Die Lösung funktioniert nicht, wenn die XML-Datei mehr als ein Tag mit demselben Namen enthält

Aus Ihrer Sicht habe ich den Code ein wenig geändert und ihn für den allgemeinen Knoten anstelle von root geschrieben:

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

Ich habe eine der Antworten nach meinem Geschmack geändert und arbeite beispielsweise mit dem folgenden XML-Code, der in der Datei XML.xml gespeichert ist, um mit mehreren Werten mit demselben Tag zu arbeiten

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

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

die Ausgabe ist

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

-2

Ich habe eine rekursive Methode, um ein Wörterbuch aus einem lxml-Element zu erhalten

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

1
Bei dieser Lösung fehlt Code, z. B. Importieren und Einrichten. Ich habe die Nachricht 'str' Objekt hat kein Attribut 'tag'
Chris Nielsen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.