Ich habe eine ganze Menge unhandlichen XML-> JSON-Codes im Web gesehen, und nachdem ich einige Zeit mit Stack-Benutzern interagiert habe, bin ich überzeugt, dass diese Menge mehr helfen kann als die ersten Google-Ergebnisseiten.
Daher analysieren wir einen Wetter-Feed und müssen Wetter-Widgets auf einer Vielzahl von Websites auffüllen. Wir untersuchen jetzt Python-basierte Lösungen.
Dieser öffentliche weather.com-RSS-Feed ist ein gutes Beispiel für das, was wir analysieren (unser eigentlicher weather.com-Feed enthält zusätzliche Informationen aufgrund einer Partnerschaft mit ihnen).
Kurz gesagt, wie sollten wir XML mithilfe von Python in JSON konvertieren?
Es gibt keine "Eins-zu-Eins" -Zuordnung zwischen XML und JSON. Daher muss für die Konvertierung einer XML in eine andere unbedingt verstanden werden, was mit den Ergebnissen do gewünscht wird.
Allerdings enthält die Standardbibliothek von Python mehrere Module zum Analysieren von XML (einschließlich DOM, SAX und ElementTree). Ab Python 2.6 ist die Unterstützung für die Konvertierung von Python-Datenstrukturen in und aus JSON im Modul json
enthalten.
Die Infrastruktur ist also da.
xmltodict (vollständige Offenlegung: Ich habe es geschrieben) kann Ihnen dabei helfen, Ihr XML in eine Dict + List + String-Struktur zu konvertieren, indem Sie diesem "Standard" folgen. Es basiert auf Expat , ist also sehr schnell und muss nicht die gesamte XML-Struktur in den Speicher laden.
Sobald Sie diese Datenstruktur haben, können Sie sie in JSON serialisieren:
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 Bibliothek xmljson zum Konvertieren mit verschiedenen XML-JSON-Konventionen verwenden.
Zum Beispiel dieses XML:
<p id="1">text</p>
übersetzt über die BadgerFish-Konvention in Folgendes:
{
'p': {
'@id': 1,
'$': 'text'
}
}
und über die GData-Konvention hinein (Attribute werden nicht unterstützt):
{
'p': {
'$t': 'text'
}
}
... und über die Parker-Konvention hinein (Attribute werden nicht unterstützt):
{
'p': 'text'
}
Es ist möglich, von XML nach JSON und von JSON nach XML mit den gleichen Konventionen Zu konvertieren:
>>> 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>]
Offenlegung: Ich habe diese Bibliothek geschrieben. Hoffe, es hilft zukünftigen Suchern.
Hier ist der Code, den ich dafür gebaut habe. Es gibt keine Analyse des Inhalts, nur eine einfache 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 __== '__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()
Es gibt eine Methode, XML-basierte Markups als JSON zu transportieren, die eine verlustfreie Konvertierung in ihre ursprüngliche Form ermöglichen. Siehe http://jsonml.org/ .
Es ist eine Art XSLT von JSON. Ich hoffe du findest es hilfreich
Wenn Sie irgendwann nur Response-Code anstelle aller Daten erhalten, dann ist error wie json parse da, also müssen Sie es als text
import xmltodict
data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json
Vielleicht möchten Sie einen Blick auf http://designtheory.org/library/extrep/designdb-1.0.pdf werfen. Dieses Projekt beginnt mit einer XML-zu-JSON-Konvertierung einer großen Bibliothek von XML-Dateien. Bei der Konvertierung wurde viel recherchiert, und es wurde das einfachste intuitive XML -> JSON-Mapping erstellt (dies wird zu Beginn des Dokuments beschrieben). Zusammengefasst, konvertieren Sie alles in ein JSON-Objekt und fügen Sie sich wiederholende Blöcke als eine Liste von Objekten ein.
objekte, die Schlüssel/Wert-Paare bedeuten (Wörterbuch in Python, Hashmap in Java, Objekt in JavaScript)
Es gibt keine Zuordnung zu XML, um ein identisches Dokument zu erhalten. Der Grund ist, dass nicht bekannt ist, ob ein Schlüssel/Wert-Paar ein Attribut oder ein <key>value</key>
war. Daher gehen diese Informationen verloren.
Wenn du mich fragst, sind Attribute ein Anfang. dann funktionierten sie wieder gut für HTML.
Nun, wahrscheinlich ist der einfachste Weg das XML in Wörterbücher zu parsen und dann mit simplejson zu serialisieren.
Ich würde vorschlagen, nicht direkt zu konvertieren. Konvertieren Sie XML in ein Objekt und anschließend vom Objekt in JSON.
Meiner Meinung nach gibt dies eine klarere Definition der Zuordnung von XML und JSON.
Es dauert eine Weile, bis Sie die richtige Lösung gefunden haben, und Sie können sogar Tools schreiben, die Ihnen bei der Erstellung eines Teils helfen, aber es würde ungefähr so 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 ):
...
Während die eingebauten Bibliotheken für das XML-Parsing ziemlich gut sind, bin ich teilweise in lxml .
Aber für das Analysieren von RSS-Feeds würde ich Universal Feed Parser empfehlen, der auch Atom ..__ analysieren kann.
Python 2.6 enthält bereits einen JSON-Parser, aber eine neuere Version mit verbesserter Geschwindigkeit ist als simplejson verfügbar.
Mit diesen Tools sollte die Erstellung Ihrer App nicht so schwierig sein.
Wenn ich irgendetwas mit XML in Python mache, verwende ich fast immer das lxml-Paket. Ich vermute, dass die meisten Leute Lxml verwenden. Sie könnten xmltodict verwenden, müssen jedoch die Strafe für das erneute Parsen der XML-Datei bezahlen.
So konvertieren Sie XML in Json mit Lxml:
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 __== "__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 aus dem eingebauten main ist:
{
"system": {
"attributes": {},
"product": {
"attributes": {},
"demod": {
"attributes": {},
"frequency": {
"attributes": {
"units": "MHz",
"value": "2.215"
},
"blah": {
"attributes": {
"value": "1"
}
}
}
}
}
}
}
Welches ist eine Umwandlung dieser XML:
<system>
<product>
<demod>
<frequency value='2.215' units='MHz'>
<blah value='1'/>
</frequency>
</demod>
</product>
</system>
Dieses Zeug hier wird aktiv gewartet und bisher ist mein Favorit: xml2json in python
Für jeden, der das noch brauchen könnte. Hier ist ein neuer, einfacher Code für diese Konvertierung.
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
Sie können declxml verwenden. Es verfügt über erweiterte Funktionen wie Multi-Attribute und komplexe verschachtelte Unterstützung. Sie müssen nur einen einfachen Prozessor dafür schreiben. Mit demselben Code können Sie auch wieder in JSON konvertieren. Es ist ziemlich unkompliziert und die Dokumentation ist großartig.
Ich fand, für einfache XML-Schnipsel, regulären Ausdruck verwenden würde Probleme sparen. 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 dies durch XML-Analyse zu tun, gibt es, wie @Dan gesagt hat, keine Einzellösung, da die Daten unterschiedlich sind. Mein Vorschlag ist, Lxml zu verwenden. Lxml.objectify ist zwar noch nicht fertig mit json, liefert aber durchaus 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'
lxml2json (Offenlegung: Ich habe es geschrieben)
https://github.com/rparelius/lxml2json
es ist sehr schnell, leicht (erfordert nur lxml) und ein Vorteil besteht darin, dass Sie steuern können, ob bestimmte Elemente in Listen oder Diagramme konvertiert werden
jsonpickle oder wenn Sie den feedparser verwenden, können Sie feed_parser_to_json.py versuchen
Meine Antwort befasst sich mit dem spezifischen (und etwas häufigen) Fall, in dem Sie müssen nicht wirklich die gesamte XML-Datei konvertieren in Json, aber was Sie brauchen, ist, bestimmte Teile der XML-Datei zu durchsuchen und auf sie zuzugreifen es soll schnell und einfach sein (mit json/dict-ähnlichen Operationen).
Zu diesem Zweck ist es wichtig zu wissen, dass das Parsen einer XML-Datei unter Verwendung von lxml
sehr schnell ist. Der langsame Teil der meisten anderen Antworten ist der zweite Durchlauf: Durchqueren der Etree-Struktur (normalerweise in Python-Land), um sie in Json umzuwandeln.
Was mich zu dem Ansatz führt, den ich für diesen Fall am besten gefunden habe: Parsen der XML-Datei mit lxml
und dann Umwickeln der etree-Knoten (träge), so dass sie mit einer dict-artigen Schnittstelle versehen werden.
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, z. B. unterstützt sie keine sauberen Fälle, in denen ein Element sowohl Text und Attribute als auch Text und Kinder hat (nur weil ich es nicht gebraucht habe, als ich es geschrieben habe ...). Es sollte einfach sein um es zu verbessern.
In meinem speziellen Anwendungsfall, bei dem ich nur bestimmte Elemente der XML-Datei verarbeiten musste, ergab dieser Ansatz eine überraschende und bemerkenswerte Beschleunigung um den Faktor 70 (!) Im Vergleich zur Verwendung von @Martin Blech xmltodict und dann direkt das Diktat durchlaufen.
Als Bonus erhalten Sie, da unsere Struktur bereits diktiert ist, eine weitere alternative Implementierung von xml2json
kostenlos. Wir müssen nur unsere diktähnliche Struktur an json.dumps
übergeben. So etwas wie:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
Wenn Ihre XML-Datei Attribute enthält, müssen Sie alphanumerische Zeichen attr_prefix
(z. B. "ATTR_") verwenden, um sicherzustellen, dass die Schlüssel gültige Json-Schlüssel sind.
Ich habe diesen Teil nicht bewertet.
Bereiten Sie Daten in Python : Vor. Um JSON zu erstellen, müssen Sie Daten in Python vorbereiten. Wir können List und Dictionary in Python verwenden, um die Daten vorzubereiten.
Python Dictionary <==> JSON Objekt (Schlüsselwertformat) Überprüfen Sie dies, um weitere Informationen zu erhalten
https://devstudioonline.com/article/create-json-and-xml-in-python