2013-06-18 11 views
6

Próbuję zrobić klasy Dict do przetworzenia xml, ale utknąć, naprawdę zabrakło pomysłów. Gdyby ktoś mógł poprowadzić ten temat, byłoby wspaniale. Kodprzekonwertować xml do python dict

opracowane do tej pory:

class XMLResponse(dict): 
    def __init__(self, xml): 
     self.result = True 
     self.message = '' 
     pass 

    def __setattr__(self, name, val): 
     self[name] = val 

    def __getattr__(self, name): 
     if name in self: 
      return self[name] 
     return None 

message="<?xml version="1.0"?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>" 
XMLResponse(message) 
+0

Wygląda na to, że na to pytanie już udzielono odpowiedzi: http://stackoverflow.com/questions/2148119/how-to-convert-an-xml-tring-to-a-dictionary-in-python – robjohncox

+1

Jakie jest twoje pożądane wyjście ? – alecxe

+0

@Josh Nie rozumiem twojego pomysłu przyjaciela – funktasmas

Odpowiedz

18

Można skorzystać z xmltodict modułu:

import xmltodict 

message = """<?xml version="1.0"?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>""" 
print xmltodict.parse(message)['note'] 

która wytwarza OrderedDict:

OrderedDict([(u'to', u'Tove'), (u'from', u'Jani'), (u'heading', u'Reminder'), (u'body', u"Don't forget me this weekend!")]) 

który można przekonwertować na dyktowanie, jeśli zamówienie nie ma znaczenia:

print dict(xmltodict.parse(message)['note']) 

Wydruki:

{u'body': u"Don't forget me this weekend!", u'to': u'Tove', u'from': u'Jani', u'heading': u'Reminder'} 
+0

dziękuję za pomoc, naprawdę to doceniam, , ale wciąż myślę, a raczej patrząc na to, jak to działało bez dodatkowego modułu, i tak spróbuję. – funktasmas

+2

@funktasmas: Jeśli chcesz zobaczyć, jak to zrobić bez dodatkowego modułu, dlaczego nie spojrzeć na źródło 'xmltodict'? To kilkaset linii czystego, dobrze skomentowanego kodu Pythona. I na pewno będzie lepiej niż jakikolwiek szybki i brudny hack, który ktoś wymyśli dla odpowiedzi na SO. – abarnert

+0

@abarnert właśnie widział, jak rozwijają moduł, może to dobry sposób na rozpoczęcie. – funktasmas

5

Należy kasy

https://github.com/martinblech/xmltodict

myślę, że jest jednym z najlepszych standardowych obsługą XML do dict widziałem.

Jednak muszę ostrzec, xml i DICT nie są całkowicie kompatybilne struktury danych

+0

dzięki za odpowiedź. Myślę, że nie są to w pełni kompatybilne struktury i że nie ma rozwiązania tak szybko, jak w przeszłości. – funktasmas

+0

@funktasmas: Jedynym poważnym problemem w prostych przypadkach jest to, że węzły XML mogą mieć atrybuty oraz podnagody i musisz zdecydować, jak je reprezentować. 'xmltodict' reprezentuje atrybuty jako węzły z przedrostkiem' @ 'na ich nazwie, co jest jednym ze sposobów rozwiązania problemu, ale istnieją inne możliwości - np. można obsłużyć węzły z '__getitem__' i attrs za pomocą' __getattr__'. – abarnert

0

Można użyć lxml library. Konwertuj ciąg znaków na obiekt xml, używając objectify.fromstring, a następnie wyszukaj metodę obiektów dir. Na przykład:

from lxml import objectify 

xml_string = """<?xml version="1.0" encoding="UTF-8"?><NewOrderResp><IndustryType></IndustryType><MessageType>R</MessageType><MerchantID>700000005894</MerchantID><TerminalID>0031</TerminalID><CardBrand>AMEX</CardBrand><AccountNum>3456732800000010</AccountNum><OrderID>TESTORDER1</OrderID><TxRefNum>55A69B278025130CD36B3A95435AA84DC45363</TxRefNum><TxRefIdx>10</TxRefIdx><ProcStatus>0</ProcStatus><ApprovalStatus>1</ApprovalStatus><RespCode></RespCode><AVSRespCode></AVSRespCode><CVV2RespCode></CVV2RespCode><AuthCode></AuthCode><RecurringAdviceCd></RecurringAdviceCd><CAVVRespCode></CAVVRespCode><StatusMsg></StatusMsg><RespMsg></RespMsg><HostRespCode></HostRespCode><HostAVSRespCode></HostAVSRespCode><HostCVV2RespCode></HostCVV2RespCode><CustomerRefNum>A51C5B2B1811E5991208</CustomerRefNum><CustomerName>BOB STEVEN</CustomerName><ProfileProcStatus>0</ProfileProcStatus><CustomerProfileMessage>Profile Created</CustomerProfileMessage><RespTime>13055</RespTime><PartialAuthOccurred></PartialAuthOccurred><RequestedAmount></RequestedAmount><RedeemedAmount></RedeemedAmount><RemainingBalance></RemainingBalance><CountryFraudFilterStatus></CountryFraudFilterStatus><IsoCountryCode></IsoCountryCode></NewOrderResp>""" 

xml_object = objectify.fromstring(xml_string) 

print xml_object.__dict__ 

Konwersja obiektu XML do dict zwróci dict:

{'RemainingBalance': u'', 'AVSRespCode': u'', 'RequestedAmount': u'', 'AccountNum': 3456732800000010, 'IsoCountryCode': u'', 'HostCVV2RespCode': u'', 'TerminalID': 31, 'CVV2RespCode': u'', 'RespMsg': u'', 'CardBrand': 'AMEX', 'MerchantID': 700000005894, 'RespCode': u'', 'ProfileProcStatus': 0, 'CustomerName': 'BOB STEVEN', 'PartialAuthOccurred': u'', 'MessageType': 'R', 'ProcStatus': 0, 'TxRefIdx': 10, 'RecurringAdviceCd': u'', 'IndustryType': u'', 'OrderID': 'TESTORDER1', 'StatusMsg': u'', 'ApprovalStatus': 1, 'RedeemedAmount': u'', 'CountryFraudFilterStatus': u'', 'TxRefNum': '55A69B278025130CD36B3A95435AA84DC45363', 'CustomerRefNum': 'A51C5B2B1811E5991208', 'CustomerProfileMessage': 'Profile Created', 'AuthCode': u'', 'RespTime': 13055, 'HostAVSRespCode': u'', 'CAVVRespCode': u'', 'HostRespCode': u''} 

Ciąg xml użyłem jest odpowiedzią z płatności Paymentech GATEWAY po prostu pokazać prawdziwy przykład światową.

Należy również zauważyć, że powyższy przykład nie jest rekursywny, więc jeśli w dyktach jest dyktatura, należy wykonać rekursję. Zobacz funkcji rekurencyjnej napisałem, że można użyć:

from lxml import objectify 

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 

def xml_to_dict(xml_str): 
    return xml_to_dict_recursion(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) 

Herezje wariant, który zachowuje klucza nadrzędnego/elementu:

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

A jeśli chcesz zwrócić tylko poddrzewa i przekształcić ją w dict można użyć Element.find():

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

Istnieje wiele możliwości, aby tego dokonać, ale ten jest super, jeśli używasz już lxml. W tym przykładzie użyto lxml-3.4.2.

3

Można by pomyśleć, że do tej pory mielibyśmy dobrą odpowiedź, ale najwyraźniej nie. Po przejrzeniu połowę kilkunastu podobnych pytań na StackOverflow, tutaj jest to, co pracował dla mnie:

from lxml import etree 
# arrow is an awesome lib for dealing with dates in python 
import arrow 


# converts an etree to dict, useful to convert xml to dict 
def etree2dict(tree): 
    root, contents = recursive_dict(tree) 
    return {root: contents} 


def recursive_dict(element): 
    if element.attrib and 'type' in element.attrib and element.attrib['type'] == "array": 
     return element.tag, [(dict(map(recursive_dict, child)) or getElementValue(child)) for child in element] 
    else: 
     return element.tag, dict(map(recursive_dict, element)) or getElementValue(element) 


def getElementValue(element): 
    if element.text: 
     if element.attrib and 'type' in element.attrib: 
      attr_type = element.attrib.get('type') 
      if attr_type == 'integer': 
       return int(element.text.strip()) 
      if attr_type == 'float': 
       return float(element.text.strip()) 
      if attr_type == 'boolean': 
       return element.text.lower().strip() == 'true' 
      if attr_type == 'datetime': 
       return arrow.get(element.text.strip()).timestamp 
     else: 
      return element.text 
    elif element.attrib: 
     if 'nil' in element.attrib: 
      return None 
     else: 
      return element.attrib 
    else: 
     return None 

i to jak go używać:

from lxml import etree 

message="""<?xml version="1.0"?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>"'' 
tree = etree.fromstring(message) 
etree2dict(tree) 

Nadzieja pomaga :-)