Converter XML para JSON usando Python?
Eu vi uma boa parte do código XML->JSON na web, e tendo interagido com os usuários do Stack por um tempo, estou convencido de que esta multidão pode ajudar mais do que as primeiras páginas dos resultados do Google podem.
Então, estamos a analisar uma fonte meteorológica, e precisamos de povoar widgets meteorológicos numa infinidade de sites. Estamos agora a investigar soluções baseadas em Python.este público weather.com RSS feed é um bom exemplo do que estaríamos a analisar (o nosso real weather.com o alimento para animais contém informação adicional devido a uma parceria w / them .
em poucas palavras, como devemos converter XML para JSON usando Python?
19 answers
Não há mapeamento" um-para-um " entre XML e JSON, por isso converter um para o outro requer necessariamente alguma compreensão do que você quer Fazer com os resultados.
Dito isto, a biblioteca padrão do Python tem vários módulos para processar XML (incluindo DOM, SAX e ElementTree). A partir do Python 2.6, o suporte para converter estruturas de dados Python de e para JSON está incluído no json
módulo
Então a infra-estrutura é la.
O Xmltodict (divulgação completa: fui eu que o escrevi) pode ajudá-lo a converter o seu XML para uma estrutura dict+list+string, seguindo este "standard". É baseado em Expat , então é muito rápido e não precisa carregar toda a árvore XML em memória.
Uma vez que você tenha essa estrutura de dados, você pode serializá-la para JSON:import xmltodict, json
o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
Você pode usar a biblioteca xmljson para converter usando diferentes convenções XML JSON .
Por exemplo, Este XML:
<p id="1">text</p>
Tradução através da Convenção de peixes texugos {[8] } para isto:
{
'p': {
'@id': 1,
'$': 'text'
}
}
E através da Convenção de GData para isto (os atributos não são suportados):
{
'p': {
'$t': 'text'
}
}
... e através da Convenção Parker para isto (os atributos não são suportados):
{
'p': 'text'
}
É possível converter de XML para JSON e de JSON a XML usando o mesmo
convenções:
>>> 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>]
Divulgação: eu escrevi esta biblioteca. Espero que ajude futuros investigadores.
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()
Existe um método para transportar a marcação baseada em XML como JSON que permite que ela seja convertida sem perdas de volta à sua forma original. Ver http://jsonml.org/.
É uma espécie de XSLT do JSON. Espero que o ache útil.É melhor dar uma vista de olhos a http://designtheory.org/library/extrep/designdb-1.0.pdf . Este projecto começa com uma conversão XML para JSON de uma grande biblioteca de ficheiros XML. Houve muita pesquisa feita na conversão, e o mais simples mapeamento intuitivo XML -> JSON foi produzido (é descrito no início do documento). Em resumo, Converter tudo para um objeto JSON, e colocar blocos repetitivos como uma lista de objetos.
Objectos que significam pares de chaves / valores (dicionário em Python, hashmap em Java, objecto em JavaScript)
Não existe nenhum mapeamento de volta ao XML para obter um documento idêntico, a razão é que é Desconhecido se um par chave/valor era um atributo ou um <key>value</key>
, portanto essa informação é perdida.
Se você me perguntar, os atributos são um hack para começar; então novamente eles funcionaram bem para HTML.
Bem, provavelmente a maneira mais simples é apenas processar o XML em dicionários e, em seguida, serializar isso com simplejson.
Embora as bibliotecas incorporadas para o processamento de XML sejam bastante boas, eu sou parcial a lxml.
Mas para analisar os feeds RSS, eu recomendaria O analisador universal de Feed , que também pode processar o átomo. Sua principal vantagem é que ele pode digerir até mesmo a maioria dos feeds malformados.
O Python 2.6 já inclui um analisador JSON, mas uma versão mais recente com velocidade melhorada está disponível como simplejson.
Com estas ferramentas a construir a aplicação, não devia ser assim. dificil.É preciso tempo para acertar e você pode até mesmo escrever Ferramentas para ajudá-lo a gerar algum dele, mas seria mais ou menos assim:
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 ):
...
Quando faço qualquer coisa com XML em python, quase sempre uso o pacote lxml. Suspeito que a maioria das pessoas usa lxml. Você poderia usar xmltodict mas você terá que pagar a penalidade de processar o XML novamente.
Para converter XML para json com lxml você:
- processar o documento XML com lxml
- Converter lxml para um dict
- converter a lista para json
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)
A saída do construído em o principal é:
{
"system": {
"attributes": {},
"product": {
"attributes": {},
"demod": {
"attributes": {},
"frequency": {
"attributes": {
"units": "MHz",
"value": "2.215"
},
"blah": {
"attributes": {
"value": "1"
}
}
}
}
}
}
}
Que é uma transformação deste xml:
<system>
<product>
<demod>
<frequency value='2.215' units='MHz'>
<blah value='1'/>
</frequency>
</demod>
</product>
</system>
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
Se algum tempo você obter apenas o código de resposta em vez de todos os dados então erro como json parse vai estar lá então u precisa convertê - lo como texto
import xmltodict
data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json
Jsonpickle ou se estiver a utilizar o feedparser, pode tentar feed_parser_to_json.py
Encontrei para snips XML simples, usar a expressão regular iria salvar problemas. Por exemplo:
# <user><name>Happy Man</name>...</user>
import re
names = re.findall(r'<name>(\w+)<\/name>', xml_string)
# do some thing to names
Para o fazer por análise XML, como @Dan disse, não existe uma solução única porque os dados são diferentes. A minha sugestão é usar lxml. Apesar de não ter terminado com o json, lxml.objectify dá bons resultados silenciosos:
>>> 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'
A Minha resposta endereços específicos (e um pouco comum) caso você não é realmente necessário para converter o xml inteiro json, mas o que você precisa fazer é atravessar/acesso a partes específicas do xml, e você precisa que ele seja rápido, e simples (usando json/dict-operações similares).
Aproximação
Para isso, é importante notar que processar um xml para o etree usando lxml
é super rápido. A parte lenta na maioria das outras respostas é a segunda passagem: atravessando a estrutura etree (geralmente em python-land), convertendo-a para json.
O que me leva à abordagem que achei melhor para este caso: analisar o xml usando {[[2]}, e então enrolar os nós etree (preguiçosamente), fornecendo-lhes uma interface tipo dict.
Código
Aqui está o 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 implementação não está completa, por exemplo, não suporta de forma limpa os casos em que um elemento tem tanto texto e atributos, ou tanto texto e crianças (apenas porque eu não precisei dele quando o escrevi...) Deve ser fácil melhorá-lo, no entanto.
Velocidade
No meu caso de uso específico, onde eu precisava apenas processar elementos específicos do xml, esta abordagem deu uma aceleração surpreendente e impressionante por um fator de 70 (!) em comparação com o uso de @Martin Blech's xmltodict e depois atravessar o dict directamente.
Bónus
Como bónus, uma vez que a nossa estrutura já é parecida com a do dict, temos outra alternativa. execução gratuita dexml2json
Só precisamos de passar a nossa estrutura parecida com o dict para json.dumps
. Algo do género:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
Se o seu xml incluir atributos, terá de utilizar alguns caracteres alfanuméricos (por exemplo, "ATTR_") para garantir que as chaves são chaves json válidas.
Ainda não fiz a avaliação desta parte.
Este material aqui é mantido activamente e até agora é o meu favorito: xml2json em python
Confira lxml2json (disclosure: I wrote it)
Https://github.com/rparelius/lxml2json
É muito rápido, leve (só requer lxml), e uma vantagem é que você tem o controle sobre se certos elementos são convertidos para listas ou dicts
Para representar os dados em JSON formato
name=John
age=20
gender=male
address=Sector 12 Greater Kailash, New Delhi
Jobs=Noida,Developer | Gurugram,Tester |Faridabad,Designer
Em JSON Nós repelimos um dado no formato chave e 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 os dados em XML formato
<!-- 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-->
Preparar dados em Python : Para criar JSON Primeiro você precisa preparar dados em python. Podemos usar lista e dicionário em Python para preparar os dados.
Dicionário Python objecto JSON (Formato do valor da chave) Assinale isto para mais detalhes
Https://devstudioonline.com/article/create-json-and-xml-in-python