Bagaimana cara mengubah string xml menjadi kamus?

125

Saya memiliki program yang membaca dokumen xml dari soket. Saya mempunyai dokumen xml yang disimpan dalam sebuah string yang ingin saya ubah secara langsung ke kamus Python, dengan cara yang sama dilakukan di simplejsonperpustakaan Django .

Ambil contoh:

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

Kemudian dic_xmlakan terlihat seperti{'person' : { 'name' : 'john', 'age' : 20 } }

pengguna361526
sumber
str memiliki beberapa kesalahan sintaks. coba: str = '<? xml version = "1.0"?> <person> <name> john </name> <age> 20 </age> </person>'
Keir

Jawaban:

58

Ini adalah modul hebat yang dibuat seseorang. Saya sudah menggunakannya beberapa kali. http://code.activestate.com/recipes/410469-xml-as-dictionary/

Berikut adalah kode dari situs web untuk berjaga-jaga jika tautannya rusak.

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

Contoh penggunaan:

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

// Atau, jika Anda ingin menggunakan string XML:

root = ElementTree.XML(xml_string)
xmldict = XmlDictConfig(root)
James
sumber
4
Anda dapat menggunakan 'xmltodict' sebagai alternatif
mrash
7
Saya mencoba ini dan ini jauh lebih cepat daripada xmltodict. Untuk mem-parsing file xml 80MB butuh waktu 7 detik, dengan xmltodict butuh waktu 90an
Eddy
1
Dikonfirmasi ... Saya belum menguji ini terhadap setiap kasus tepi tetapi untuk string XML saya yang agak tidak rumit, ini cukup cepat (sekitar 8 kali lebih cepat dari xmltodictperpustakaan). Kerugiannya adalah Anda harus menyimpannya sendiri dalam proyek Anda.
Dirk
10
Halo, ini berfungsi dengan sempurna, hanya akan menambahkan cuplikan untuk mereka yang tidak dapat menemukan cElementTree, cukup ubah baris pertama ke: from xml.etree import cElementTree as ElementTree
Rafael Aguilar
2
Tidak memilih karena ada jawaban yang lebih baik yang diposting di bawah ini, terutama dalam menangani beberapa tag dengan nama yang sama.
Maksym
280

xmltodict (pengungkapan penuh: Saya yang menulisnya) melakukan persis seperti itu:

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
sumber
22
Ini adalah modul yang luar biasa.
zekel
2
Anda baru saja menyelamatkan saya dari banyak usaha. Membuatku bahagia.
LRE
3
juga, untuk googlenauts di masa mendatang - Saya dapat menggunakan ini di App Engine, yang selama ini saya yakini tidak berfungsi dengan baik dengan sebagian besar pustaka xml dengan Python.
LRE
2
U hanya menunjukkan itu disimpan string unicode. Itu tidak memengaruhi nilai string dengan cara apa pun.
Joshua Olson
2
Bagus. Dan ya, @ypercube, ada fungsi xmldict.unparse () untuk kebalikannya.
Duther
47

Cuplikan XML-to-Python-dict berikut mem-parse entitas serta atribut yang mengikuti "spesifikasi" XML-ke-JSON ini . Ini adalah solusi paling umum yang menangani semua kasus 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

Ini digunakan:

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

Output dari contoh ini (sesuai "spesifikasi" yang ditautkan di atas) harus:

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

Tidak selalu cantik, tetapi tidak ambigu, dan masukan XML yang lebih sederhana menghasilkan JSON yang lebih sederhana. :)


Memperbarui

Jika Anda ingin melakukan sebaliknya , keluarkan string XML dari JSON / dict , Anda dapat menggunakan:

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
sumber
1
Terima kasih untuk kode ini! Info tambahan: jika Anda menggunakan python 2.5 Anda tidak dapat menggunakan pemahaman kamus, jadi Anda harus mengubah baris d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} ke d = { t.tag: dict( (k, v[0] if len(v) == 1 else v) for k, v in dd.iteritems() ) }
M--
2
Saya telah menguji hampir 10 potongan / modul python / dll untuk itu. Yang ini adalah yang terbaik yang pernah saya temukan. Menurut pengujian saya, ini: 1) jauh lebih cepat daripada github.com/martinblech/xmltodict (berdasarkan api XML SAX) 2) lebih baik daripada github.com/mcspring/XML2Dict yang memiliki beberapa masalah kecil ketika beberapa anak memiliki nama yang sama 3 ) lebih baik daripada code.activestate.com/recipes/410469-xml-as-dictionary yang juga memiliki masalah kecil dan lebih penting: 4) kode yang jauh lebih pendek daripada yang sebelumnya! Terima kasih @ K3 --- rnc
Basj
Sejauh ini, ini adalah jawaban yang paling komprehensif, dan berfungsi pada> 2.6, dan cukup fleksibel. satu-satunya masalah saya adalah bahwa teks dapat berubah di mana ia berada tergantung pada apakah ada atribut atau tidak). saya memposting solusi yang lebih kecil dan lebih kaku juga.
Erik Aronesty
1
Jika Anda perlu mendapatkan dikt yang dipesan dari file XML, silakan, Anda dapat menggunakan contoh yang sama ini dengan sedikit modifikasi (lihat tanggapan saya di bawah): stackoverflow.com/questions/2148119/…
serfer2
Ini juga cukup bagus dan cepat saat digunakan dengan cElementTreeatau lxml.etree. Perhatikan bahwa saat menggunakan Python 3, semua .iteritems()harus diubah menjadi .items()(perilaku yang sama tetapi kata kunci diubah dari Python 2 ke 3).
Dirk
25

Versi ringan ini, meskipun tidak dapat dikonfigurasi, cukup mudah untuk disesuaikan sesuai kebutuhan, dan berfungsi pada ular sanca tua. Juga kaku - artinya hasilnya sama terlepas dari keberadaan atribut.

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

Begitu:

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

dictify(root)

Hasil dalam:

{'erik': {'a': [{'x': '1', '_text': 'v'}, {'y': '2', '_text': 'w'}]}}
Erik Aronesty
sumber
2
Saya suka solusi ini. Sederhana dan tidak membutuhkan libs eksternal.
MattK
6

Versi terbaru dari pustaka PicklingTools (1.3.0 dan 1.3.1) mendukung alat untuk mengonversi dari XML ke dikt Python.

Unduhan tersedia di sini: PicklingTools 1.3.1

Ada cukup banyak dokumentasi untuk konverter di sini : dokumentasi menjelaskan secara rinci semua keputusan dan masalah yang akan muncul saat mengonversi antara kamus XML dan Python (ada sejumlah kasus edge: atribut, daftar, daftar anonim, anonim dicts, eval, dll. yang tidak ditangani oleh kebanyakan konverter). Secara umum, konverter mudah digunakan. Jika 'example.xml' berisi:

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

Kemudian untuk mengubahnya menjadi kamus:

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

Ada alat untuk mengonversi di C ++ dan Python: C ++ dan Python melakukan konversi indentikal, tetapi C ++ sekitar 60x lebih cepat

rts1
sumber
tentu saja, jika ada 2 a, ini bukan format yang baik.
Erik Aronesty
1
Kelihatannya menarik, tetapi saya belum menemukan cara penggunaan PicklingTools - apakah ini hanya kumpulan file kode sumber dari mana saya harus menemukan yang tepat untuk pekerjaan saya dan kemudian menyalinnya ke dalam proyek saya? Tidak ada modul untuk dimuat atau sesuatu yang lebih sederhana?
Dirk
Saya mendapatkan: di peekIntoNextNWSChar c = self.is .read (1) AttributeError: objek 'str' tidak memiliki atribut 'read'
sqp_125
5

Anda dapat melakukannya dengan cukup mudah dengan lxml. Pertama instal:

[sudo] pip install lxml

Berikut adalah fungsi rekursif yang saya tulis yang melakukan pekerjaan berat untuk Anda:

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)

Varian di bawah ini mempertahankan kunci / elemen induk:

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

Jika Anda hanya ingin mengembalikan subtree dan mengonversinya menjadi dict, Anda dapat menggunakan Element.find () untuk mendapatkan subtree lalu mengonversinya:

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

Lihat dokumen lxml di sini . Saya harap ini membantu!

radtek
sumber
5

Penafian: Pengurai XML yang dimodifikasi ini terinspirasi oleh Adam Clark . Pengurai XML asli berfungsi untuk sebagian besar kasus sederhana. Namun, itu tidak berfungsi untuk beberapa file XML yang rumit. Saya men-debug kode baris demi baris dan akhirnya memperbaiki beberapa masalah. Jika Anda menemukan beberapa bug, beri tahu saya. Saya senang memperbaikinya.

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)    
harimau
sumber
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
sumber
2

Parser XML yang paling mudah digunakan untuk Python adalah ElementTree (mulai 2.5x dan di atasnya ada di pustaka standar xml.etree.ElementTree). Saya tidak berpikir ada sesuatu yang melakukan apa yang Anda inginkan di luar kotak. Akan sangat sepele untuk menulis sesuatu untuk melakukan apa yang Anda inginkan menggunakan ElementTree, tapi mengapa mengonversi ke kamus, dan mengapa tidak menggunakan ElementTree secara langsung.


sumber
2

Kode dari http://code.activestate.com/recipes/410469-xml-as-dictionary/ berfungsi dengan baik, tetapi jika ada beberapa elemen yang sama di tempat tertentu dalam hierarki, kode itu hanya menimpanya.

Saya menambahkan shim antara yang terlihat untuk melihat apakah elemen sudah ada sebelum self.update (). Jika demikian, munculkan entri yang ada dan buat daftar dari yang sudah ada dan yang baru. Setiap duplikat berikutnya ditambahkan ke daftar.

Tidak yakin apakah ini dapat ditangani dengan lebih anggun, tetapi berhasil:

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

Dari @ K3 --- respons rnc (yang terbaik untuk saya) saya telah menambahkan sedikit modifikasi untuk mendapatkan OrderedDict dari teks XML (terkadang urutan penting):

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

Mengikuti contoh @ K3 --- rnc, Anda dapat menggunakannya:

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

Semoga membantu;)

serfer2
sumber
1

Berikut tautan ke solusi ActiveState - dan kode jika hilang lagi.

==================================================
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
sumber
Ya itu. Telah mereproduksi kode di sini kalau-kalau kode itu berlaku lagi.
Jamie Banteng
0

Pada satu titik saya harus mengurai dan menulis XML yang hanya terdiri dari elemen tanpa atribut sehingga pemetaan 1: 1 dari XML ke dikt dapat dilakukan dengan mudah. Inilah yang saya temukan jika orang lain juga tidak membutuhkan atribut:

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

@dibrovsd: Solusi tidak akan berfungsi jika xml memiliki lebih dari satu tag dengan nama yang sama

Sesuai pemikiran Anda, saya telah sedikit memodifikasi kode dan menulisnya untuk node umum, bukan 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
hal2455
sumber
0

Saya telah mengubah salah satu jawaban sesuai selera saya dan bekerja dengan banyak nilai dengan tag yang sama misalnya pertimbangkan kode xml berikut yang disimpan dalam 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>

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

hasilnya adalah

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

Saya memiliki metode rekursif untuk mendapatkan kamus dari elemen lxml

    def recursive_dict(element):
        return (element.tag.split('}')[1],
                dict(map(recursive_dict, element.getchildren()),
                     **element.attrib))
moylop260.dll
sumber
1
Solusi ini kehilangan beberapa kode, seperti impor dan penyiapan. Saya mendapat pesan 'str' object tidak memiliki atribut 'tag'
Chris Nielsen