Konvertieren von XML zu JSON mit Python?
Frage
Ich habe einen fairen Anteil an ungainly XML-> JSON-Code auf der Web zu sehen ist, und mit Stapel Nutzer für ein bisschen interagierten zu haben, ich bin überzeugt, dass diese Menge mehr als die ersten paar Seiten von Google Ergebnissen helfen kann .
Also, wir Parsen ein Wetter-Feed, und wir müssen Wetter Widgets auf dem eine Vielzahl von Web-Sites füllen. Wir suchen jetzt in Python-basierten Lösungen.
Dieses öffentliche weather.com RSS-Feed ein gutes Beispiel dafür, was würden wir werden das Parsen ( unser tatsächlicher weather.com Feed enthält zusätzliche Informationen aufgrund einer Partnerschaft w / sie ).
Auf den Punkt gebracht, wie wir XML zu JSON mit Python konvertieren sollte?
Lösung
Es gibt keine "one-to-one" Mapping zwischen XML und JSON, so eine Umwandlung in die andere erfordert notwendigerweise ein gewisses Verständnis von dem, was Sie wollen tun mit den Ergebnissen.
aber sagen, dass Python-Standardbibliothek hat mehrere Module für das Parsen von XML ( einschließlich DOM, SAX und ElementTree). Ab Python 2.6, Unterstützung zu Python Datenstrukturen und zum Umwandeln von JSON ist in dem json
Modul enthält .
So ist die Infrastruktur ist da.
Andere Tipps
xmltodict (vollständige Offenlegung: Ich schrieb es) kann Ihnen helfen, Ihre XML in eine dict + Liste konvertieren + String-Struktur, im Anschluss an diese “ Standard ". Es ist Expat -Basis, also ist es sehr schnell und braucht nicht das ganze zu laden XML-Struktur im Speicher.
Wenn Sie diese Datenstruktur haben, können Sie es zu JSON serialisiert:
import xmltodict, json
o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
Sie können die xmljson Bibliothek mit verschiedenem XML-JSON Konventionen .
Zum Beispiel dieses XML:
<p id="1">text</p>
übersetzt über die BadgerFish Konvention in diesen:
{
'p': {
'@id': 1,
'$': 'text'
}
}
und über die GData Konvention in diese (Attribute werden nicht unterstützt):
{
'p': {
'$t': 'text'
}
}
... und über die Parker Konvention in diese (Attribute werden nicht unterstützt):
{
'p': 'text'
}
Es ist möglich, von XML zu JSON und von JSON in XML zu konvertieren, die diese verwenden Konventionen:
>>> import json, xmljson
>>> from lxml.etree import fromstring, tostring
>>> xml = fromstring('<p id="1">text</p>')
>>> json.dumps(xmljson.badgerfish.data(xml))
'{"p": {"@id": 1, "$": "text"}}'
>>> xmljson.parker.etree({'ul': {'li': [1, 2]}})
# Creates [<ul><li>1</li><li>2</li></ul>]
Disclosure: Ich diese Bibliothek geschrieben. Hoffe, dass es künftig Suchenden hilft.
Hier ist der Code, den ich dafür gebaut. Es gibt keinen Parsing der Inhalte, einfach nur Konvertierung.
from xml.dom import minidom
import simplejson as json
def parse_element(element):
dict_data = dict()
if element.nodeType == element.TEXT_NODE:
dict_data['data'] = element.data
if element.nodeType not in [element.TEXT_NODE, element.DOCUMENT_NODE,
element.DOCUMENT_TYPE_NODE]:
for item in element.attributes.items():
dict_data[item[0]] = item[1]
if element.nodeType not in [element.TEXT_NODE, element.DOCUMENT_TYPE_NODE]:
for child in element.childNodes:
child_name, child_dict = parse_element(child)
if child_name in dict_data:
try:
dict_data[child_name].append(child_dict)
except AttributeError:
dict_data[child_name] = [dict_data[child_name], child_dict]
else:
dict_data[child_name] = child_dict
return element.nodeName, dict_data
if __name__ == '__main__':
dom = minidom.parse('data.xml')
f = open('data.json', 'w')
f.write(json.dumps(parse_element(dom), sort_keys=True, indent=4))
f.close()
Wenn einige Zeit erhalten Sie nur Antwortcode , also nicht alle Daten dann Fehler wie json Parse wird es so u konvertieren müssen es als Text
import xmltodict
data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json
Es ist eine Methode, XML-basierte Markup als JSON zu transportieren, die es verlustlos wieder in seine ursprüngliche Form umgewandelt werden können. Siehe http://jsonml.org/ .
Es ist eine Art von XSLT von JSON. Ich hoffe, Sie finden es hilfreich,
Sie können einen Blick auf http://designtheory.org/library/ haben wollen extrep / Designdb-1.0.pdf . Das Projekt beginnt mit einer XML zu JSON Konvertierung von einer großen Bibliothek von XML-Dateien ab. Es gab viel Forschung in der Konvertierung durchgeführt, und die einfachste intuitive XML -> JSON-Mapping wurde hergestellt (es früh in dem Dokument beschrieben wird). Zusammenfassend alles auf ein JSON-Objekt konvertieren, und als eine Liste von Objekten zu wiederholen Blöcke setzen.
Objekte bedeuten, Schlüssel / Wert-Paare (Wörterbuch in Python, hashmap in Java, Objekt in JavaScript)
Es gibt keine Zuordnung zu XML zurück ein identisches Dokument zu erhalten, ist der Grund, es ist nicht bekannt, ob ein Schlüssel / Wert-Paar war ein Attribut oder ein <key>value</key>
daher, dass Informationen verloren gehen.
Wenn Sie mich fragen, sind Attribute ein Hack zu beginnen; dann wieder arbeitete sie auch für HTML.
Nun, wahrscheinlich der einfachste Weg ist nur das XML in Wörterbücher analysiert und dann die Serialisierung mit simplejson.
Ich würde vorschlagen, nicht für eine direkte Umwandlung gehen. Konvertieren von XML in ein Objekt, dann von dem Objekt zu JSON.
Meiner Meinung nach ist dies gibt eine sauberere Definition, wie die XML und JSON entsprechen.
Es braucht Zeit, richtig zu machen und Sie können sogar Werkzeuge schreiben, Ihnen zu helfen mit etwas davon zu erzeugen, aber es wäre in etwa wie folgt aussehen:
class Channel:
def __init__(self)
self.items = []
self.title = ""
def from_xml( self, xml_node ):
self.title = xml_node.xpath("title/text()")[0]
for x in xml_node.xpath("item"):
item = Item()
item.from_xml( x )
self.items.append( item )
def to_json( self ):
retval = {}
retval['title'] = title
retval['items'] = []
for x in items:
retval.append( x.to_json() )
return retval
class Item:
def __init__(self):
...
def from_xml( self, xml_node ):
...
def to_json( self ):
...
fand ich für eine einfache XML snips verwenden regulären Ausdruck Schwierigkeiten retten würde. Zum Beispiel:
# <user><name>Happy Man</name>...</user>
import re
names = re.findall(r'<name>(\w+)<\/name>', xml_string)
# do some thing to names
Um es durch XML-Analyse zu tun, wie @ Dan sagte, gibt es nicht ein-for-all-Lösung, da die Daten unterschiedlich ist. Mein Vorschlag ist, lxml zu verwenden. Obwohl nicht auf json abgeschlossen, lxml.objectify geben ruhig gute Ergebnisse:
>>> from lxml import objectify
>>> root = objectify.fromstring("""
... <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
... <a attr1="foo" attr2="bar">1</a>
... <a>1.2</a>
... <b>1</b>
... <b>true</b>
... <c>what?</c>
... <d xsi:nil="true"/>
... </root>
... """)
>>> print(str(root))
root = None [ObjectifiedElement]
a = 1 [IntElement]
* attr1 = 'foo'
* attr2 = 'bar'
a = 1.2 [FloatElement]
b = 1 [IntElement]
b = True [BoolElement]
c = 'what?' [StringElement]
d = None [NoneElement]
* xsi:nil = 'true'
Während der Einbau-Libs für das Parsen von XML ist recht gut ich bin teilweise zu lxml .
Aber für RSS-Feeds Parsen, würde ich empfehlen Universal-Feed-Parser , die auch analysieren Atom. Sein Hauptvorteil ist, dass es sogar die meisten malformed Feeds verdauen kann.
Python 2.6 enthält bereits einen JSON-Parser, aber eine neuere Version mit verbesserter Geschwindigkeit ist als simplejson .
Mit diesen Tools, um Ihren App Aufbau sollte nicht so schwierig sein.
Wenn ich etwas tun mit XML in Python fast immer ich das lxml Paket verwenden. Ich vermute, dass die meisten Menschen lxml verwenden. Sie könnten xmltodict verwenden, aber Sie werden wieder die Strafe der Parsen der XML bezahlen.
Zur Umwandlung von XML zu JSON mit lxml Sie:
- Parse XML-Dokument mit lxml
- Konvertieren lxml zu einem dict
- Konvertieren Liste json
Ich verwende die folgende Klasse in meinen Projekten. Verwenden Sie die toJson Methode.
from lxml import etree
import json
class Element:
'''
Wrapper on the etree.Element class. Extends functionality to output element
as a dictionary.
'''
def __init__(self, element):
'''
:param: element a normal etree.Element instance
'''
self.element = element
def toDict(self):
'''
Returns the element as a dictionary. This includes all child elements.
'''
rval = {
self.element.tag: {
'attributes': dict(self.element.items()),
},
}
for child in self.element:
rval[self.element.tag].update(Element(child).toDict())
return rval
class XmlDocument:
'''
Wraps lxml to provide:
- cleaner access to some common lxml.etree functions
- converter from XML to dict
- converter from XML to json
'''
def __init__(self, xml = '<empty/>', filename=None):
'''
There are two ways to initialize the XmlDocument contents:
- String
- File
You don't have to initialize the XmlDocument during instantiation
though. You can do it later with the 'set' method. If you choose to
initialize later XmlDocument will be initialized with "<empty/>".
:param: xml Set this argument if you want to parse from a string.
:param: filename Set this argument if you want to parse from a file.
'''
self.set(xml, filename)
def set(self, xml=None, filename=None):
'''
Use this to set or reset the contents of the XmlDocument.
:param: xml Set this argument if you want to parse from a string.
:param: filename Set this argument if you want to parse from a file.
'''
if filename is not None:
self.tree = etree.parse(filename)
self.root = self.tree.getroot()
else:
self.root = etree.fromstring(xml)
self.tree = etree.ElementTree(self.root)
def dump(self):
etree.dump(self.root)
def getXml(self):
'''
return document as a string
'''
return etree.tostring(self.root)
def xpath(self, xpath):
'''
Return elements that match the given xpath.
:param: xpath
'''
return self.tree.xpath(xpath);
def nodes(self):
'''
Return all elements
'''
return self.root.iter('*')
def toDict(self):
'''
Convert to a python dictionary
'''
return Element(self.root).toDict()
def toJson(self, indent=None):
'''
Convert to JSON
'''
return json.dumps(self.toDict(), indent=indent)
if __name__ == "__main__":
xml='''<system>
<product>
<demod>
<frequency value='2.215' units='MHz'>
<blah value='1'/>
</frequency>
</demod>
</product>
</system>
'''
doc = XmlDocument(xml)
print doc.toJson(indent=4)
Die Ausgabe von dem eingebauten Haupt ist:
{
"system": {
"attributes": {},
"product": {
"attributes": {},
"demod": {
"attributes": {},
"frequency": {
"attributes": {
"units": "MHz",
"value": "2.215"
},
"blah": {
"attributes": {
"value": "1"
}
}
}
}
}
}
}
Welche ist eine Transformation dieser xml:
<system>
<product>
<demod>
<frequency value='2.215' units='MHz'>
<blah value='1'/>
</frequency>
</demod>
</product>
</system>
jsonpickle oder wenn Sie feedparser verwenden, können Sie versuchen, feed_parser_to_json.py
Meine Antwort auf die spezifischen (und etwas gemeinsam) Fall, in dem Sie auf nicht wirklich das gesamte XML konvertieren müssen zu json, aber was Sie brauchen, ist / Zugriff bestimmte Teile des xml zu durchqueren , und Sie müssen es schnell und einfach (unter Verwendung von json / dict-ähnlichen Behandlungen) sein.
Ansatz
Dabei ist es wichtig zu beachten, dass ein XML-Parsing etree lxml
Verwendung ist super schnell. Der langsame Teil in den meisten anderen Antworten ist den zweiten Durchgang. Die etree Struktur durchquert (in der Regel in Python-Land), um es zu json Umwandlung
Was mich zu dem Ansatz führt ich am besten für diesen Fall gefunden. Parsen der XML-lxml
verwenden und Einwickeln dann die etree Knoten (träge), so dass sie mit einer dict-ähnlicher Oberfläche bietet
Code
Hier ist der Code:
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),
)
Diese Implementierung ist nicht vollständig, zum Beispiel, ist es nicht sauber Fälle unterstützen, in denen ein Element sowohl Text hat und Attribut oder Text und Kinder (nur, weil ich es nicht brauchte, als ich es geschrieben hätte ...) Es leicht zu verbessern, sollte es aber.
Geschwindigkeit
In meinem speziellen Anwendungsfall, wo ich nur prozessspezifische Elemente des XML benötigt, gab diesen Ansatz eine überraschende und auffallend Speedup um den Faktor 70 (!) im Vergleich zu @ Martin Blech ist mit xmltodict und dann die dict direkt durchquert.
Bonus
Als Bonus, da unsere Struktur bereits dict-like ist, bekommen wir eine weitere alternative Implementierung von xml2json
kostenlos. Wir müssen nur unsere dict artige Struktur zu übergeben json.dumps
. So etwas wie:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
Wenn Sie Ihre XML-Attribut enthält, müssen Sie einige alphanumerischen attr_prefix
(beispielsweise „ATTR_“) verwenden, um sicherzustellen, ist die Schlüssel gültig json Tasten.
Ich habe diesen Teil nicht gebenchmarkt.
hier Dieses Zeug ist aktiv gepflegt und so weit ist mein Favorit: xml2json in Python
Für jeden, der diese noch benötigen. Hier ist ein neuer, einfacher Code, um diese Umwandlung zu tun.
from xml.etree import ElementTree as ET
xml = ET.parse('FILE_NAME.xml')
parsed = parseXmlToJson(xml)
def parseXmlToJson(xml):
response = {}
for child in list(xml):
if len(list(child)) > 0:
response[child.tag] = parseXmlToJson(child)
else:
response[child.tag] = child.text or ''
# one-liner equivalent
# response[child.tag] = parseXmlToJson(child) if len(list(child)) > 0 else child.text or ''
return response
Besuche lxml2json (Offenlegung: Ich habe es geschrieben)
https://github.com/rparelius/lxml2json
es ist sehr schnell, leicht (nur erfordert lxml), und ein Vorteil ist, dass Sie die Kontrolle darüber haben, ob bestimmte Elemente in Listen oder dicts umgewandelt werden
Sie können declxml verwenden. Es verfügt über erweiterte Funktionen wie Multi-Attribute und komplex verschachtelte Unterstützung. Sie brauchen nur einen einfachen Prozessor für sie zu schreiben. Auch mit dem gleichen Code, können Sie konvertieren und zu JSON zurück. Es ist recht einfach und die Dokumentation ist genial.
Bereiten Sie Daten in Python : So erstellen Sie JSON zuerst müssen Sie Daten in Python vorzubereiten. Wir können Liste und Wörterbuch in Python verwenden, um die Daten vor.
Python Wörterbuch <==> JSON Object (Key Value Format) Aktivieren Sie diese Option, um weitere Informationen
https://devstudioonline.com/article/create-json -and-xml-in-python
Für die Darstellung von Daten in JSON Format
name=John
age=20
gender=male
address=Sector 12 Greater Kailash, New Delhi
Jobs=Noida,Developer | Gurugram,Tester |Faridabad,Designer
json wir Daten in Schlüssel repesent und Wert-Format
{
"name":"john",
"age":20,
"gender":"male",
"address":["New kP college","Greater Kailash","New Delhi"],
"jobs":[
{"Place":"Noida","Title":"Developer "},
{"Place":"Gurugram","Title":"Tester "},
{"Place":"Faridabad","Title":"Designer"}
]
}
Für die Darstellung von Daten in XML Format
<!-- In xml we write a code under a key you can take any key -->
<info> <!-- key open -->
<name> john </name>
<age> 20 </age>
<gender> male </gender>
<address>
<item> New kP college </item>
<item> Greater Kailash </item>
<item> New Delhi </item>
</address>
<jobs>
<item>
<title>Developer </title>
<place>Noida</place>
</item>
<item>
<title>Designer</title>
<place>Gurugram</place>
</item>
<item>
<title>Developer </title>
<place>Faridabad</place>
</item>
</jobs>
</info> <!-- key close-->