web-dev-qa-db-de.com

XML in JSON mit Python konvertieren?

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?

137
Pete Karl II

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.

51
Dan Lenski

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"]}}'
246
Martin Blech

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.

16
S Anand

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()
7
Paulo Vj

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 

6
themihai

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 
6
Akshay Kumbhar

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.

5
pykler

Nun, wahrscheinlich ist der einfachste Weg das XML in Wörterbücher zu parsen und dann mit simplejson zu serialisieren. 

4
dguaraglia

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 ):
    ...
3

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.

2
Luka Marinko

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:

  1. XML-Dokument mit lxml analysieren
  2. Konvertiere lxml in ein Dikt
  3. Konvertieren Sie die Liste in 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 __== "__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>
2
shrewmouse

Dieses Zeug hier wird aktiv gewartet und bisher ist mein Favorit: xml2json in python

1
typelogic

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
1
jnhustin

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.

Link: https://declxml.readthedocs.io/de/latest/index.html

1
srth12

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'
1
Andrew_1510

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

1
Robert Parelius

jsonpickle oder wenn Sie den feedparser verwenden, können Sie feed_parser_to_json.py versuchen

1
choonkeat

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

Ansatz

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.

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, 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.

Geschwindigkeit

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.

Bonus

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.

1
shx2

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 List <==> JSON Array

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

0
Anushree Anisha