Làm cách nào để chuyển đổi một chuỗi xml thành một từ điển?


125

Tôi có một chương trình đọc tài liệu xml từ ổ cắm. Tôi có tài liệu xml được lưu trữ trong một chuỗi mà tôi muốn chuyển đổi trực tiếp sang từ điển Python, giống như cách nó được thực hiện trong simplejsonthư viện của Django .

Lấy một ví dụ:

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

Sau đó dic_xmlsẽ trông như{'person' : { 'name' : 'john', 'age' : 20 } }


str có một vài lỗi cú pháp. thử: str = '<? xml version = "1.0"?> <person> <name> john </ name> <age> 20 </ age> </ person>'
Keir

Câu trả lời:


58

Đây là một mô-đun tuyệt vời mà ai đó tạo ra. Tôi đã sử dụng nó nhiều lần. http://code.activestate.com/recipes/410469-xml-as-dipedia/

Đây là mã từ trang web trong trường hợp liên kết bị hỏng.

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

Ví dụ sử dụng:

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

// Hoặc, nếu bạn muốn sử dụng chuỗi XML:

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

4
Bạn có thể sử dụng 'xmltodict' thay thế
mrash

7
Tôi đã thử điều này và nó nhanh hơn nhiều so với xmltodict. Để phân tích tệp xml 80 MB, phải mất 7 giây, với xmltodict, phải mất 90 giây
Eddy

1
Xác nhận ... Tôi chưa thử nghiệm điều này với mọi trường hợp cạnh nhưng đối với các chuỗi XML khá phức tạp của tôi, việc này khá nhanh (nhanh hơn khoảng 8 lần so với xmltodictthư viện). Nhược điểm là bạn phải tự lưu trữ nó trong dự án của bạn.
Dirk

10
Xin chào, điều này hoạt động hoàn hảo, sẽ chỉ thêm một đoạn trích cho những người không thể tìm thấy cElementTree, chỉ cần thay đổi dòng đầu tiên thành: from xml.etree import cElementTree as ElementTree
Rafael Aguilar

2
Bỏ phiếu xuống vì có câu trả lời tốt hơn được đăng dưới đây, đặc biệt là trong việc xử lý nhiều thẻ có cùng tên.
Maksym

279

xmltodict (tiết lộ đầy đủ: tôi đã viết nó) thực hiện chính xác điều đó:

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
Đây là một mô-đun tuyệt vời.
zekel

2
bạn vừa tiết kiệm cho tôi rất nhiều nỗ lực. Làm cho ngày của tôi.
LRE

3
Ngoài ra, đối với các googlenauts trong tương lai - tôi đã có thể sử dụng điều này trong App Engine, điều mà tôi đã tin rằng không chơi tốt với hầu hết các thư viện xml trong Python.
LRE

2
Các u chỉ cho thấy nó được lưu trữ chuỗi unicode. Nó không ảnh hưởng đến giá trị của chuỗi theo bất kỳ cách nào.
Joshua Olson

2
Đẹp. Và vâng, @ypercube, có một hàm xmldict.unparse () cho đảo ngược.
Duther

47

Đoạn mã XML-to-Python-dict sau đây phân tích cú pháp các thực thể cũng như các thuộc tính theo "đặc tả" XML-to-JSON này . Đây là giải pháp chung nhất xử lý tất cả các trường hợp của 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

Nó được sử dụng:

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

Đầu ra của ví dụ này (theo "đặc tả" được liên kết ở trên) phải là:

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

Không nhất thiết phải đẹp, nhưng nó không rõ ràng và các đầu vào XML đơn giản hơn dẫn đến JSON đơn giản hơn. :)


Cập nhật

Nếu bạn muốn làm ngược lại , hãy phát ra một chuỗi XML từ JSON / dict , bạn có thể sử dụng:

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
Thx cho mã này! Thông tin bổ sung: nếu bạn sử dụng python 2.5, bạn không thể sử dụng khả năng hiểu từ điển, vì vậy bạn phải thay đổi dòng d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} thành d = { t.tag: dict( (k, v[0] if len(v) == 1 else v) for k, v in dd.iteritems() ) }
M--

2
Tôi đã thử nghiệm gần 10 đoạn / mô-đun python / vv cho điều đó. Đây là một trong những tốt nhất tôi đã tìm thấy. Theo thử nghiệm của tôi, đó là: 1) nhanh hơn nhiều so với github.com/martinblech/xmltodict (dựa trên XML SAX api) 2) tốt hơn github.com/mcspring/XML2Dict có một số vấn đề nhỏ khi một số trẻ em có cùng tên 3 ) tốt hơn code.activestate.com/recipes/410469-xml-as-dipedia có vấn đề nhỏ và quan trọng hơn: 4) mã ngắn hơn nhiều so với tất cả các mã trước đó! Cảm ơn @ K3 --- rnc
Basj

Đây là, câu trả lời toàn diện nhất, và nó hoạt động trên> 2.6, và nó khá linh hoạt. Vấn đề duy nhất của tôi là văn bản có thể thay đổi nơi nó cư trú tùy thuộc vào việc có thuộc tính hay không). tôi đã đăng một giải pháp thậm chí nhỏ hơn và cứng nhắc hơn.
Erik Aronesty

1
Nếu bạn cần lấy một lệnh chính tả từ một tệp XML, xin vui lòng, bạn có thể sử dụng ví dụ tương tự này với một vài sửa đổi (xem phản hồi của tôi bên dưới): stackoverflow.com/questions/2148119/
Khăn

Điều này cũng khá tiện lợi và nhanh chóng khi được sử dụng với cElementTreehoặc lxml.etree. Lưu ý rằng khi sử dụng Python 3, tất cả .iteritems()phải được thay đổi thành .items()(cùng hành vi nhưng từ khóa đã thay đổi từ Python 2 thành 3).
Dirk

25

Phiên bản nhẹ này, trong khi không thể cấu hình, khá dễ dàng để điều chỉnh khi cần thiết, và hoạt động trong những con trăn cũ. Ngoài ra, nó cứng nhắc - có nghĩa là kết quả là như nhau bất kể sự tồn tại của các thuộc tính.

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

Vì thế:

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

dictify(root)

Kết quả trong:

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

2
Tôi thích giải pháp này. Đơn giản và không yêu cầu libs bên ngoài.
MattK

6

Các phiên bản mới nhất của các thư viện PicklingTools (1.3.0 và 1.3.1) các công cụ hỗ trợ để chuyển đổi từ XML sang một lệnh Python.

Tải về có sẵn ở đây: PicklingTools 1.3.1

Có khá nhiều tài liệu cho các trình chuyển đổi ở đây : tài liệu mô tả chi tiết tất cả các quyết định và vấn đề sẽ phát sinh khi chuyển đổi giữa các từ điển XML và Python (có một số trường hợp cạnh: thuộc tính, danh sách, danh sách ẩn danh, ẩn danh, ẩn danh dicts, eval, vv mà hầu hết các bộ chuyển đổi không xử lý). Nhìn chung, mặc dù, các bộ chuyển đổi rất dễ sử dụng. Nếu một 'example.xml' chứa:

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

Sau đó, để chuyển đổi nó thành một từ điển:

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

Có các công cụ để chuyển đổi trong cả C ++ và Python: C ++ và Python thực hiện chuyển đổi thụt lề, nhưng C ++ nhanh hơn khoảng 60 lần


Tất nhiên, sau đó nếu có 2 a, đây không phải là một định dạng tốt.
Erik Aronesty

1
Trông có vẻ thú vị, nhưng tôi vẫn chưa tìm ra cách sử dụng PicklingTools - đây có phải là một tarball của các tệp mã nguồn mà tôi phải tìm đúng cho công việc của mình và sau đó sao chép chúng vào dự án của mình không? Không có mô-đun để tải hoặc bất cứ điều gì đơn giản hơn?
Dirk

Tôi nhận được: in peekIntoNextNWSChar c = self.is .read (1) AttributionError: đối tượng 'str' không có thuộc tính 'read'
sqp_125

5

Bạn có thể làm điều này khá dễ dàng với lxml. Đầu tiên cài đặt nó:

[sudo] pip install lxml

Đây là một hàm đệ quy mà tôi đã viết, nó mang lại sức nặng cho bạn:

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)

Biến thể dưới đây bảo tồn khóa / phần tử cha:

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

Nếu bạn muốn chỉ trả lại một cây con và chuyển đổi nó thành dict, bạn có thể sử dụng Element.find () để lấy cây con và sau đó chuyển đổi nó:

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

Xem các tài liệu lxml ở đây . Tôi hi vọng cái này giúp được!


5

Tuyên bố miễn trừ trách nhiệm: Trình phân tích cú pháp XML đã sửa đổi này được lấy cảm hứng từ Adam Clark Trình phân tích cú pháp XML gốc hoạt động cho hầu hết các trường hợp đơn giản. Tuy nhiên, nó không hoạt động đối với một số tệp XML phức tạp. Tôi đã gỡ lỗi từng dòng mã và cuối cùng đã sửa một số vấn đề. Nếu bạn tìm thấy một số lỗi, xin vui lòng cho tôi biết. Tôi vui mừng để sửa nó.

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

Trình phân tích cú pháp XML dễ sử dụng nhất cho Python là ElementTree (từ 2,5 lần trở lên, nó nằm trong thư viện chuẩn xml.etree.EuityTree). Tôi không nghĩ rằng có bất cứ điều gì làm chính xác những gì bạn muốn ra khỏi hộp. Sẽ thật tầm thường khi viết một cái gì đó để làm những gì bạn muốn bằng ElementTree, nhưng tại sao lại chuyển đổi thành từ điển và tại sao không sử dụng trực tiếp ElementTree.


2

Mã từ http://code.activestate.com/recipes/410469-xml-as-dipedia/ hoạt động tốt, nhưng nếu có nhiều yếu tố giống nhau tại một vị trí nhất định trong cấu trúc phân cấp, nó sẽ ghi đè lên chúng.

Tôi đã thêm một shim giữa vẻ ngoài đó để xem phần tử đã tồn tại trước self.update (). Nếu vậy, bật mục hiện có và tạo một danh sách ra khỏi mục hiện có và mới. Bất kỳ bản sao tiếp theo được thêm vào danh sách.

Không chắc chắn nếu điều này có thể được xử lý duyên dáng hơn, nhưng nó hoạt động:

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

Từ @ K3 --- phản hồi rnc (tốt nhất cho tôi) Tôi đã thêm một sửa đổi nhỏ để nhận OrderedDict từ văn bản XML (một số lần vấn đề đặt hàng):

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

Theo ví dụ @ K3 --- rnc, bạn có thể sử dụng nó:

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

Hy vọng nó giúp ;)


1

Đây là một liên kết đến một giải pháp ActiveState - và mã trong trường hợp nó biến mất một lần nữa.

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

Vâng, đúng vậy. Đã sao chép mã ở đây trong trường hợp nó đi một lần nữa.
Jamie Bull

0

Tại một thời điểm, tôi đã phải phân tích và viết XML chỉ bao gồm các phần tử không có thuộc tính để có thể dễ dàng ánh xạ 1: 1 từ XML sang dict. Đây là những gì tôi nghĩ ra trong trường hợp người khác cũng không cần thuộc tính:

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: Giải pháp sẽ không hoạt động nếu xml có nhiều hơn một thẻ có cùng tên

Theo dòng suy nghĩ của bạn, tôi đã sửa đổi mã một chút và viết nó cho nút chung thay vì 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

Tôi đã sửa đổi một trong những câu trả lời theo sở thích của mình và để làm việc với nhiều giá trị với cùng một thẻ, ví dụ, hãy xem xét mã xml sau được lưu trong tệp 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>

và trong trăn

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)

đầu ra là

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

-2

Tôi có một phương thức đệ quy để lấy từ điển từ phần tử lxml

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

1
Giải pháp này thiếu một số mã, chẳng hạn như nhập và thiết lập. Tôi nhận được thông báo 'str' đối tượng không có thuộc tính 'tag'
Chris Nielsen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.