Domanda

Ho visto una buona dose di XML sgraziato - > codice JSON sul web e dopo aver interagito con gli utenti di Stack per un po ', sono convinto che questa folla possa aiutare più delle prime pagine dei risultati di Google può.

Quindi, stiamo analizzando un feed meteorologico e dobbiamo popolare i widget meteo su una moltitudine di siti web. Stiamo esaminando ora le soluzioni basate su Python.

Questo pubblico Il feed RSS di weather.com è un buon esempio di ciò che analizzeremo ( il nostro feed weather.com reale contiene informazioni aggiuntive a causa di una partnership con loro ).

In poche parole, come dovremmo convertire XML in JSON usando Python?

È stato utile?

Soluzione

Non esiste un "uno a uno" mappatura tra XML e JSON, quindi la conversione l'una nell'altra richiede necessariamente una certa comprensione di ciò che si desidera fare con i risultati.

Detto questo, la libreria standard di Python ha diversi moduli per l'analisi di XML ( tra cui DOM, SAX ed ElementTree). A partire da Python 2.6, il supporto per la conversione delle strutture dati Python da e verso JSON è incluso nel json module .

Quindi l'infrastruttura è lì.

Altri suggerimenti

xmltodict (divulgazione completa: l'ho scritto) può aiutarti a convertire il tuo XML in dict + list + struttura delle stringhe, seguendo questo " ; standard di " . È Expat , quindi è molto veloce e non è necessario caricare l'intero Albero XML in memoria.

Una volta che hai quella struttura di dati, puoi serializzarla su JSON:

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'

Puoi utilizzare la libreria xmljson per convertire utilizzando Convenzioni XML JSON .

Ad esempio, questo XML:

<p id="1">text</p>

si traduce tramite la convenzione BadgerFish in questo:

{
  'p': {
    '@id': 1,
    '

e tramite la convenzione GData in questo (gli attributi non sono supportati):

{
  'p': {
    '$t': 'text'
  }
}

... e tramite la convenzione Parker in questo (gli attributi non sono supportati):

{
  'p': 'text'
}

È possibile convertire da XML a JSON e da JSON a XML usando lo stesso convenzioni:

>>> 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, "<*>quot;: "text"}}'
>>> xmljson.parker.etree({'ul': {'li': [1, 2]}})
# Creates [<ul><li>1</li><li>2</li></ul>]

Divulgazione: ho scritto questa libreria. Spero che aiuti i futuri ricercatori.

: 'text' } }

e tramite la convenzione GData in questo (gli attributi non sono supportati):

<*>

... e tramite la convenzione Parker in questo (gli attributi non sono supportati):

<*>

È possibile convertire da XML a JSON e da JSON a XML usando lo stesso convenzioni:

<*>

Divulgazione: ho scritto questa libreria. Spero che aiuti i futuri ricercatori.

Ecco il codice che ho creato per questo. Non è possibile analizzare i contenuti, solo una semplice conversione.

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

Se qualche volta ottieni solo codice di risposta invece di tutti i dati, errore come json parse sarà lì, quindi devi convertirlo come testo

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 

Esiste un metodo per trasportare il markup basato su XML come JSON che consente di convertirlo senza perdita nella sua forma originale. Vedi http://jsonml.org/ .

È una specie di XSLT di JSON. Spero che lo trovi utile

Potresti dare un'occhiata a http://designtheory.org/library/ extrep / Designdb-1.0.pdf . Questo progetto inizia con una conversione da XML a JSON di una grande libreria di file XML. Sono state fatte molte ricerche nella conversione e l'XML più semplice e intuitivo - > È stata prodotta la mappatura JSON (è descritta all'inizio del documento). In sintesi, converti tutto in un oggetto JSON e inserisci i blocchi ripetuti come un elenco di oggetti.

oggetti che significano coppie chiave / valore (dizionario in Python, hashmap in Java, oggetto in JavaScript)

Non è possibile eseguire il mapping su XML per ottenere un documento identico, il motivo è che non è noto se una coppia chiave / valore fosse un attributo o < key > value < / key > , quindi che le informazioni vengono perse.

Se mi chiedi, gli attributi sono un hack per iniziare; poi di nuovo hanno funzionato bene per HTML.

Bene, probabilmente il modo più semplice è semplicemente analizzare l'XML in dizionari e quindi serializzarlo con simplejson.

Suggerirei di non optare per una conversione diretta. Converti XML in un oggetto, quindi dall'oggetto in JSON.

A mio avviso, ciò fornisce una definizione più chiara di come XML e JSON corrispondono.

Ci vuole tempo per avere ragione e potresti persino scrivere strumenti per aiutarti a generarne una parte, ma sarebbe simile a questo:

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

Ho trovato per semplici snapshot XML, l'uso dell'espressione regolare risparmierebbe problemi. Ad esempio:

# <user><name>Happy Man</name>...</user>
import re
names = re.findall(r'<name>(\w+)<\/name>', xml_string)
# do some thing to names

Per farlo analizzando XML, come diceva @Dan, non esiste una soluzione unica perché i dati sono diversi. Il mio consiglio è di usare lxml. Sebbene non sia finito per json, lxml.objectify danno buoni risultati tranquilli:

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

Mentre le librerie integrate per l'analisi XML sono abbastanza buone, sono parziale a lxml .

Ma per l'analisi dei feed RSS, consiglierei Universal Feed Parser , che può anche analizzare Atom. Il suo principale vantaggio è che può digerire anche la maggior parte dei mangimi malformati.

Python 2.6 include già un parser JSON, ma una più recente con velocità migliorata è disponibile come simplejson .

Con questi strumenti costruire la tua app non dovrebbe essere così difficile.

Quando faccio qualsiasi cosa con XML in Python uso quasi sempre il pacchetto lxml. Sospetto che la maggior parte delle persone usi lxml. Puoi usare xmltodict ma dovrai pagare la penalità di analizzare nuovamente l'XML.

Per convertire XML in json con lxml tu:

  1. Documento XML di analisi con lxml
  2. Converti lxml in un dict
  3. Converti elenco in json

Uso la seguente classe nei miei progetti. Usa il metodo toJson.

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)

L'output dal principale incorporato è:

{
    "system": {
        "attributes": {}, 
        "product": {
            "attributes": {}, 
            "demod": {
                "attributes": {}, 
                "frequency": {
                    "attributes": {
                        "units": "MHz", 
                        "value": "2.215"
                    }, 
                    "blah": {
                        "attributes": {
                            "value": "1"
                        }
                    }
                }
            }
        }
    }
}

Quale è una trasformazione di questo xml:

<system>
    <product>
        <demod>
            <frequency value='2.215' units='MHz'>
                <blah value='1'/>
            </frequency>
        </demod>
    </product>
</system>

jsonpickle o se stai usando feedparser, puoi provare feed_parser_to_json.py

La mia risposta affronta il caso specifico (e piuttosto comune) in cui non hai davvero bisogno di convertire l'intero xml in json, ma quello che ti serve è attraversare / accedere a parti specifiche dell'xml e è necessario che sia veloce e semplice (utilizzando operazioni simili a json / dict).

Approccio

Per questo, è importante notare che l'analisi di un XML per etree usando lxml è super veloce. La parte lenta nella maggior parte delle altre risposte è il secondo passaggio: attraversare la struttura etree (di solito in terra di pitone), convertendola in json.

Il che mi porta all'approccio che ho trovato migliore per questo caso: analizzare l'xml usando lxml e quindi avvolgere i nodi etree (pigramente), fornendo loro un'interfaccia simile a dict.

Codice

Ecco il codice:

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

Questa implementazione non è completa, ad esempio, non supporta in modo chiaro i casi in cui un elemento ha sia testo che attributi o testo e figli (solo perché non ne avevo bisogno quando l'ho scritto ...) dovrebbe essere facile migliorarlo, tuttavia.

Velocità

Nel mio caso d'uso specifico, in cui avevo bisogno di elaborare solo elementi specifici dell'xml, questo approccio ha dato una sorprendente e sorprendente velocità di un fattore di 70 (!) rispetto all'utilizzo di @Martin Blech xmltodict e quindi attraversando direttamente il dict.

Bonus

Come bonus, dal momento che la nostra struttura è già simile a una regola, otteniamo un'altra implementazione alternativa di xml2json gratuitamente. Dobbiamo solo passare la nostra struttura simile a dict a json.dumps . Qualcosa del tipo:

def xml_to_json(xmlstr, **kwargs):
    x = xml_to_dictlike(xmlstr, **kwargs)
    return json.dumps(x)

Se il tuo xml include attributi, devi utilizzare alcuni attr_prefix alfanumerici (ad es. " ATTR_ "), per assicurarti che le chiavi siano chiavi json valide.

Non ho effettuato il benchmark di questa parte.

Questa roba qui è attivamente mantenuta e finora è la mia preferita: xml2json in python

A chiunque possa averne ancora bisogno. Ecco un codice più recente e semplice per eseguire questa conversione.

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

controlla lxml2json (divulgazione: l'ho scritto)

https://github.com/rparelius/lxml2json

è molto veloce, leggero (richiede solo lxml) e uno dei vantaggi è che hai il controllo sul fatto che determinati elementi vengano convertiti in elenchi o dicts

Puoi usare declxml. Ha funzionalità avanzate come multi attributi e supporto nidificato complesso. Devi solo scrivere un semplice processore per questo. Inoltre, con lo stesso codice, è possibile riconvertire anche in JSON. È abbastanza semplice e la documentazione è fantastica.

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

Prepara i dati in Python : Per creare JSON devi prima preparare i dati in Python. Possiamo usare Elenco e dizionario in Python per preparare i dati.

Python Elenco < == > JSON Array

Python Dizionario < == > JSON Object (Key Value Format) Controlla questo per maggiori dettagli

https://devstudioonline.com/article/create-json -e-xml-in-python

Per rappresentare i dati nel JSON

name=John
age=20
gender=male
address=Sector 12 Greater Kailash, New Delhi
Jobs=Noida,Developer | Gurugram,Tester |Faridabad,Designer

In json rispondiamo un dato in chiave e formato valore

{
    "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"}
           ]
}

Per rappresentare i dati in XML

<!-- 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-->

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top