मेरा जवाब विशिष्ट (और कुछ सामान्य) मामले को संबोधित करता है जहाँ आपको वास्तव में पूरे xml को json में बदलने की आवश्यकता नहीं है , लेकिन आपको xml के विशिष्ट भागों को ट्रैक / एक्सेस करने के लिए क्या आवश्यक है, और आपको इसे तेज़ करने की आवश्यकता है , और सरल (json / तानाशाह की तरह संचालन का उपयोग करके)।
पहुंच
इसके लिए, यह ध्यान रखना महत्वपूर्ण है कि एक्सएमएल का उपयोग करने के लिए एक्सएमएल का उपयोग lxml
करना सुपर फास्ट है। अन्य उत्तरों में से अधिकांश में धीमा हिस्सा दूसरा पास है: एट्री संरचना का पता लगाना (आमतौर पर अजगर-भूमि में), इसे जसन में परिवर्तित करना।
जो मुझे उस दृष्टिकोण के लिए ले जाता है जिसे मैंने इस मामले के लिए सबसे अच्छा पाया: एक्सएमएल का उपयोग करके पार्सिंग करना lxml
, और फिर एट्री नोड्स (लेज़ीली) को लपेटकर, उन्हें एक तानाशाही जैसा इंटरफ़ेस प्रदान करना।
कोड
यहाँ कोड है:
from collections import Mapping
import lxml.etree
class ETreeDictWrapper(Mapping):
def __init__(self, elem, attr_prefix = '@', list_tags = ()):
self.elem = elem
self.attr_prefix = attr_prefix
self.list_tags = list_tags
def _wrap(self, e):
if isinstance(e, basestring):
return e
if len(e) == 0 and len(e.attrib) == 0:
return e.text
return type(self)(
e,
attr_prefix = self.attr_prefix,
list_tags = self.list_tags,
)
def __getitem__(self, key):
if key.startswith(self.attr_prefix):
return self.elem.attrib[key[len(self.attr_prefix):]]
else:
subelems = [ e for e in self.elem.iterchildren() if e.tag == key ]
if len(subelems) > 1 or key in self.list_tags:
return [ self._wrap(x) for x in subelems ]
elif len(subelems) == 1:
return self._wrap(subelems[0])
else:
raise KeyError(key)
def __iter__(self):
return iter(set( k.tag for k in self.elem) |
set( self.attr_prefix + k for k in self.elem.attrib ))
def __len__(self):
return len(self.elem) + len(self.elem.attrib)
# defining __contains__ is not necessary, but improves speed
def __contains__(self, key):
if key.startswith(self.attr_prefix):
return key[len(self.attr_prefix):] in self.elem.attrib
else:
return any( e.tag == key for e in self.elem.iterchildren() )
def xml_to_dictlike(xmlstr, attr_prefix = '@', list_tags = ()):
t = lxml.etree.fromstring(xmlstr)
return ETreeDictWrapper(
t,
attr_prefix = '@',
list_tags = set(list_tags),
)
यह कार्यान्वयन पूर्ण नहीं है, उदाहरण के लिए, यह उन मामलों का सफाई से समर्थन नहीं करता है, जहां किसी तत्व में पाठ और विशेषताएँ दोनों हैं, या दोनों पाठ और बच्चे (केवल इसलिए कि मुझे इसकी आवश्यकता नहीं थी जब मैंने इसे लिखा था ...) यह आसान होना चाहिए यद्यपि इसे सुधारना है।
गति
मेरे विशिष्ट उपयोग के मामले में, जहां मुझे केवल xml के विशिष्ट तत्वों को संसाधित करने की आवश्यकता थी, इस दृष्टिकोण ने @Martin Blech के xmltodict का उपयोग करने की तुलना में 70 और (फिर!) को सीधे करने के लिए एक सुपरराइजिंग और हड़ताली स्पीडअप दिया ।
बक्शीश
एक बोनस के रूप में, चूंकि हमारी संरचना पहले से ही तानाशाह की तरह है, हमें xml2json
मुफ्त में एक और वैकल्पिक कार्यान्वयन मिलता है । हमें बस अपनी तानाशाही जैसी संरचना को पारित करने की आवश्यकता है json.dumps
। कुछ इस तरह:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
यदि आपकी xml में विशेषताएँ शामिल हैं, तो आपको attr_prefix
कुंजी जोंस कुंजियाँ मान्य करने के लिए कुछ अल्फ़ान्यूमेरिक (जैसे "ATTR_") का उपयोग करने की आवश्यकता होगी ।
मैंने इस हिस्से को बेंचमार्क नहीं किया है।