Cevabım , tüm xml'yi json'a dönüştürmeniz gerekmediği belirli (ve biraz yaygın) bir durumu ele alıyor , ancak ihtiyacınız olan xml'nin belirli bölümlerini çaprazlamak / erişmek ve hızlı olması gerekiyor ve basit (json / dict benzeri işlemler kullanarak).
Yaklaşmak
Bunun için, bir xml'yi kullanarak etree'yi ayrıştırmanın lxml
süper hızlı olduğunu not etmek önemlidir . Diğer cevapların çoğundaki yavaş kısım ikinci geçiştir: etree yapısının (genellikle python-land'da) geçilmesi, json'a dönüştürülmesi.
Bu da beni bu durum için en iyi bulduğum yaklaşıma götürüyor: lxml
xml'yi kullanarak ayrıştırma ve etree düğümlerini (tembel olarak) sarmalama, bunlara dikte benzeri bir arayüz sağlama.
kod
İşte kod:
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),
)
Bu uygulama tam değildir, örneğin, bir öğenin hem metin hem de niteliklere veya hem metin hem de çocuklara sahip olduğu durumları temiz bir şekilde desteklemez (yalnızca yazdığımda ihtiyacım olmadığı için ...) Kolay olmalı yine de geliştirmek için.
hız
Yalnızca xml'in belirli öğelerini işlemem gerektiğinde özel kullanım durumumda, bu yaklaşım @Martin Blech'in xmltodict'ini kullanmaya ve ardından doğrudan dikdörtgeni geçmeye kıyasla 70 (!) Faktörle şaşırtıcı ve çarpıcı bir hız kazandı .
Bonus
Bir bonus olarak, yapımız zaten dikte olduğu xml2json
için, ücretsiz olarak başka bir alternatif uygulama elde ediyoruz . Sadece diktine benzer yapımızı geçirmemiz gerekiyor json.dumps
. Gibi bir şey:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
attr_prefix
XML'niz öznitelikler içeriyorsa , anahtarların geçerli json anahtarları olduğundan emin olmak için bazı alfasayısal (örn. "ATTR_") kullanmanız gerekir .
Bu kısmı karşılaştırmadım.