[python] XML 문자열을 사전으로 변환하는 방법은 무엇입니까?
소켓에서 xml 문서를 읽는 프로그램이 있습니다. Django의 simplejson
라이브러리 에서 수행되는 것과 동일한 방식으로 Python 사전으로 직접 변환하려는 문자열에 저장된 xml 문서가 있습니다.
예를 들어 :
str ="<?xml version="1.0" ?><person><name>john</name><age>20</age></person"
dic_xml = convert_to_dic(str)
그러면 다음 dic_xml
과 같이 보일 것입니다.{'person' : { 'name' : 'john', 'age' : 20 } }
답변
이것은 누군가가 만든 훌륭한 모듈입니다. 나는 그것을 여러 번 사용했습니다.
http://code.activestate.com/recipes/410469-xml-as-dictionary/
다음은 링크가 잘못 될 경우를 대비하여 웹 사이트의 코드입니다.
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})
사용 예 :
tree = ElementTree.parse('your_file.xml')
root = tree.getroot()
xmldict = XmlDictConfig(root)
// 또는 XML 문자열을 사용하려는 경우 :
root = ElementTree.XML(xml_string)
xmldict = XmlDictConfig(root)
답변
xmltodict (전체 공개 : 내가 썼습니다)는 정확히 다음과 같이합니다.
xmltodict.parse("""
<?xml version="1.0" ?>
<person>
<name>john</name>
<age>20</age>
</person>""")
# {u'person': {u'age': u'20', u'name': u'john'}}
답변
다음 XML-to-Python-dict 스 니펫은 이 XML-to-JSON “사양”을 따르는 속성뿐만 아니라 엔티티를 구문 분석 합니다. 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
다음과 같이 사용됩니다.
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))
이 예제의 출력 (위에 링크 된 “사양”에 따라)은 다음과 같아야합니다.
{'root': {'e': [None,
'text',
{'@name': 'value'},
{'#text': 'text', '@name': 'value'},
{'a': 'text', 'b': 'text'},
{'a': ['text', 'text']},
{'#text': 'text', 'a': 'text'}]}}
반드시 예쁘지는 않지만 모호하지 않으며 더 간단한 XML 입력으로 더 간단한 JSON이 생성됩니다. 🙂
최신 정보
당신이하고 싶은 경우 역을 , 방출 JSON / DICT에서 XML 문자열을 , 당신은 사용할 수 있습니다 :
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))
답변
이 경량 버전은 구성 할 수 없지만 필요에 따라 조정하기가 매우 쉽고 오래된 파이썬에서 작동합니다. 또한 엄격합니다. 즉, 속성의 존재 여부에 관계없이 결과가 동일합니다.
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
그래서:
root = ET.fromstring("<erik><a x='1'>v</a><a y='2'>w</a></erik>")
dictify(root)
결과 :
{'erik': {'a': [{'x': '1', '_text': 'v'}, {'y': '2', '_text': 'w'}]}}
답변
최신 버전의 PicklingTools 라이브러리 (1.3.0 및 1.3.1)는 XML에서 Python dict로 변환하는 도구를 지원합니다.
여기에서 다운로드 할 수 있습니다. PicklingTools 1.3.1
여기 에 변환기에 대한 문서가 많이 있습니다 . 문서는 XML과 Python 사전간에 변환 할 때 발생하는 모든 결정과 문제에 대해 자세히 설명합니다 (특성, 목록, 익명 목록, 익명의 여러 경우가 있습니다. 대부분의 변환기가 처리하지 않는 dicts, eval 등). 그러나 일반적으로 변환기는 사용하기 쉽습니다. ‘example.xml’에 다음이 포함 된 경우 :
<top>
<a>1</a>
<b>2.2</b>
<c>three</c>
</top>
그런 다음 사전으로 변환하려면 :
>>> 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 ++와 Python 모두에서 변환하는 도구가 있습니다. C ++와 Python은 indentical 변환을 수행하지만 C ++는 약 60 배 더 빠릅니다.
답변
lxml을 사용하면이 작업을 매우 쉽게 수행 할 수 있습니다. 먼저 설치하십시오.
[sudo] pip install lxml
여기에 내가 작성한 재귀 함수가 있습니다.
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)
아래 변형은 상위 키 / 요소를 유지합니다.
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)}
하위 트리 만 반환하고 dict로 변환하려는 경우 Element.find () 를 사용 하여 하위 트리를 가져온 다음 변환 할 수 있습니다.
xml_obj.find('.//') # lxml.objectify.ObjectifiedElement instance
답변
면책 조항 :이 수정 된 XML 파서는 Adam Clark에 의해 영감을 받았습니다
. 원래 XML 파서는 대부분의 간단한 경우에 작동합니다. 그러나 일부 복잡한 XML 파일에서는 작동하지 않았습니다. 코드를 한 줄씩 디버깅하고 마침내 몇 가지 문제를 해결했습니다. 버그를 발견하면 알려주세요. 나는 그것을 고쳐서 기쁘다.
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)
