Pregunta

He visto una buena cantidad de código desgarbado XML - > JSON en la web, y después de haber interactuado un poco con los usuarios de Stack, estoy convencido de que esta multitud puede ayudar más que las primeras páginas de los resultados de Google pueden.

Por lo tanto, estamos analizando una fuente de información meteorológica, y necesitamos llenar los widgets del tiempo en una multitud de sitios web. Estamos buscando soluciones basadas en Python.

Este público El feed RSS de weather.com es un buen ejemplo de lo que estaríamos analizando ( nuestro feed weather.com real contiene información adicional debido a una asociación con ellos ).

En pocas palabras, ¿cómo debemos convertir XML a JSON usando Python?

¿Fue útil?

Solución

No hay "uno a uno" mapeo entre XML y JSON, por lo que la conversión de uno a otro necesariamente requiere cierta comprensión de lo que desea hacer con los resultados.

Dicho esto, la biblioteca estándar de Python tiene varios módulos para analizar XML ( incluidos DOM, SAX y ElementTree). A partir de Python 2.6, el soporte para convertir estructuras de datos de Python a y desde JSON se incluye en el json módulo .

Entonces la infraestructura está ahí.

Otros consejos

xmltodict (divulgación completa: lo escribí) puede ayudarlo a convertir su XML en una lista dict + + estructura de cadena, siguiendo esto " ; estándar " . Está Expat , por lo que es muy rápido y no necesita cargar todo Árbol XML en memoria.

Una vez que tenga esa estructura de datos, puede serializarla a JSON:

import xmltodict, json

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

Puede usar la biblioteca xmljson para convertir utilizando diferentes Convenciones XML JSON .

Por ejemplo, este XML:

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

se traduce a través de la Convención de BadgerFish en esto:

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

y a través de la convención GData en esto (los atributos no son compatibles):

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

... y a través de la Convención de Parker en esto (los atributos no son compatibles):

{
  'p': 'text'
}

Es posible convertir de XML a JSON y de JSON a XML usando el mismo convenciones:

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

Divulgación: escribí esta biblioteca. Espero que ayude a los futuros buscadores.

: 'text' } }

y a través de la convención GData en esto (los atributos no son compatibles):

<*>

... y a través de la Convención de Parker en esto (los atributos no son compatibles):

<*>

Es posible convertir de XML a JSON y de JSON a XML usando el mismo convenciones:

<*>

Divulgación: escribí esta biblioteca. Espero que ayude a los futuros buscadores.

Aquí está el código que construí para eso. No hay análisis de los contenidos, solo conversión simple.

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

Si en algún momento obtienes solo código de respuesta en lugar de todos los datos, entonces aparecerá error como json parse , por lo que debes convertirlo como texto

import xmltodict

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

Hay un método para transportar el marcado basado en XML como JSON que le permite volver a su forma original sin pérdidas. Consulte http://jsonml.org/ .

Es una especie de XSLT de JSON. Espero que les sea útil

Es posible que desee echar un vistazo a http://designtheory.org/library/ extrep / designdb-1.0.pdf . Este proyecto comienza con una conversión de XML a JSON de una gran biblioteca de archivos XML. Se realizó mucha investigación en la conversión, y el XML intuitivo más simple: > Se produjo el mapeo JSON (se describe al principio del documento). En resumen, convierta todo a un objeto JSON y coloque bloques repetidos como una lista de objetos.

objetos que significan pares clave / valor (diccionario en Python, hashmap en Java, objeto en JavaScript)

No hay asignación de regreso a XML para obtener un documento idéntico, la razón es que no se sabe si un par clave / valor era un atributo o un < key > value < / key > , por lo tanto, esa información se pierde.

Si me preguntas, los atributos son un truco para comenzar; entonces de nuevo funcionaron bien para HTML.

Bueno, probablemente la forma más sencilla es analizar el XML en los diccionarios y luego serializarlo con simplejson.

Sugeriría no realizar una conversión directa. Convierta XML a un objeto, luego del objeto a JSON.

En mi opinión, esto proporciona una definición más clara de cómo se corresponden el XML y el JSON.

Lleva tiempo hacerlo bien e incluso puede escribir herramientas para ayudarlo a generar parte de él, pero se vería más o menos así:

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

Encontré recortes XML simples, el uso de expresiones regulares ahorraría problemas. Por ejemplo:

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

Para hacerlo mediante análisis XML, como dijo @Dan, no existe una solución única porque los datos son diferentes. Mi sugerencia es usar lxml. Aunque no ha terminado con json, lxml.objectify da buenos resultados:

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

Si bien las bibliotecas incorporadas para el análisis XML son bastante buenas, soy parcial a lxml .

Pero para analizar las fuentes RSS, recomendaría Universal Feed Parser , que también puede analizar Atom. Su principal ventaja es que puede digerir incluso la mayoría de los alimentos malformados.

Python 2.6 ya incluye un analizador JSON, pero una versión más nueva de con velocidad mejorada está disponible como simplejson .

Con estas herramientas, crear su aplicación no debería ser tan difícil.

Cuando hago algo con XML en python, casi siempre uso el paquete lxml. Sospecho que la mayoría de la gente usa lxml. Puede usar xmltodict pero tendrá que pagar la penalización de analizar el XML nuevamente.

Para convertir XML a json con lxml usted:

  1. Analizar documento XML con lxml
  2. Convertir lxml a un dict
  3. Convertir lista a json

Uso la siguiente clase en mis proyectos. Use el método 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)

La salida del main integrado es:

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

Que es una transformación de este xml:

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

jsonpickle o si está utilizando feedparser, puede probar feed_parser_to_json.py

Mi respuesta aborda el caso específico (y algo común) en el que realmente no necesita convertir todo el xml a json, pero lo que necesita es atravesar / acceder a partes específicas del xml , y necesita que sea rápido y simple (utilizando operaciones json / dict-like).

Enfoque

Para esto, es importante tener en cuenta que analizar un xml a etree usando lxml es súper rápido. La parte lenta en la mayoría de las otras respuestas es el segundo paso: atravesar la estructura etree (generalmente en python-land), convertirla a json.

Lo que me lleva al enfoque que encontré mejor para este caso: analizar el xml usando lxml , y luego envolver los nodos etree (perezosamente), proporcionándoles una interfaz tipo dict.

Código

Aquí está el código:

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

Esta implementación no está completa, por ejemplo, no admite casos en los que un elemento tiene texto y atributos, o texto y elementos secundarios (solo porque no lo necesitaba cuando lo escribí ...) Sin embargo, debería ser fácil mejorarlo.

Velocidad

En mi caso de uso específico, donde solo necesitaba procesar elementos específicos del xml, este enfoque dio una aceleración sorprendente y por un factor de 70 (!) en comparación con el uso de @Martin Blech xmltodict y luego atraviesa el dict directamente.

Bonus

Como beneficio adicional, dado que nuestra estructura ya es similar a un dict, obtenemos otra implementación alternativa de xml2json de forma gratuita. Solo necesitamos pasar nuestra estructura tipo dict a json.dumps . Algo así como:

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

Si su xml incluye atributos, necesitaría usar algunos attr_prefix alfanuméricos (por ejemplo " ATTR_ "), para asegurarse de que las claves son claves json válidas.

No he evaluado esta parte.

Este material aquí se mantiene activamente y hasta ahora es mi favorito: xml2json en python

A cualquiera que aún pueda necesitar esto. Aquí hay un código más nuevo y simple para hacer esta conversión.

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

echa un vistazo a lxml2json (divulgación: lo escribí)

https://github.com/rparelius/lxml2json

es muy rápido, liviano (solo requiere lxml), y una ventaja es que tiene control sobre si ciertos elementos se convierten en listas o dictados

Puede usar declxml. Tiene características avanzadas como atributos múltiples y soporte anidado complejo. Solo necesita escribir un procesador simple para ello. También con el mismo código, también puede volver a convertir a JSON. Es bastante sencillo y la documentación es impresionante.

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

Prepare los datos en Python : Para crear JSON primero debe preparar los datos en python. Podemos usar List and Dictionary en Python para preparar los datos.

Python List < == > JSON Array

Python Diccionario < == > JSON Objeto (formato de valor clave) Comprueba esto para más detalles

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

Para representar datos en formato JSON

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

En json repetimos datos en clave y formato de valor

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

Para representar datos en formato 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-->

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top