Преобразование XML в JSON с помощью Python?
Я видел изрядную долю неуклюжего кода XML - >JSON в интернете, и, немного пообщавшись с пользователями Stack, я убежден, что эта толпа может помочь больше, чем первые несколько страниц результатов Google.
Итак, мы анализируем фид погоды, и нам нужно заполнить погодные виджеты на множестве веб-сайтов. Сейчас мы изучаем решения на основе Python.
эта публика weather.com RSS-канал это хороший пример того, что мы будем разбирать (наш фактический weather.com лента содержит дополнительную информацию из-за партнерства с ними).
в двух словах, как мы должны конвертировать XML в JSON с помощью Python?
19 ответов:
между XML и JSON нет сопоставления" один к одному", поэтому преобразование одного в другой обязательно требует некоторого понимания того, что вы хотите do С результатами.
как говорится, стандартная библиотека Python имеет несколько модулей для разбора XML (включая DOM, SAX и ElementTree). Начиная с Python 2.6, поддержка преобразования структур данных Python в JSON и из JSON включена в
jsonмодуль.Так инфраструктура там есть.
xmltodict (полное раскрытие: я написал это) может помочь вам преобразовать XML в структуру dict + list+string, следуя этому "стандартные". Это экспат-based, так что это очень быстро и не нужно загружать все XML-дерево в памяти.
Как только у вас есть эта структура данных, вы можете сериализовать ее в JSON:
import xmltodict, json o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>') json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
можно использовать xmljson библиотека для преобразования с помощью различных XML JSON conventions.
например, это XML:
<p id="1">text</p>переводит через Барсучья конвенция в:
{ 'p': { '@id': 1, '$': 'text' } }и через Конвенция GData в это (атрибуты не поддерживаются):
{ 'p': { '$t': 'text' } }... и через Parker convention в это (атрибуты не являются поддерживается):
{ 'p': 'text' }можно конвертировать из XML в JSON и из JSON в XML, используя то же самое условные обозначения:
>>> 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>]раскрытие: я написал эту библиотеку. Надеюсь, это поможет будущим поисковикам.
вот код, который я построил для этого. Там нет разбора содержимого,просто преобразование.
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()
существует метод транспортировки разметки на основе XML в виде JSON, который позволяет без потерь преобразовать ее обратно в исходную форму. См.http://jsonml.org/.
Это какой-то XSLT-код в формате JSON. Я надеюсь, что вы найдете его полезным
вы можете взглянуть на http://designtheory.org/library/extrep/designdb-1.0.pdf. Этот проект начинается с преобразования XML в JSON большой библиотеки XML-файлов. Было проведено много исследований в преобразовании, и было создано самое простое интуитивное отображение XML -> JSON (оно описано в начале документа). Таким образом, преобразуйте все в объект JSON и поместите повторяющиеся блоки в виде списка объектов.
объекты, значение пары ключ/значение (словарь на Python, hashmap на Java, объект на JavaScript)
нет сопоставления обратно в XML, чтобы получить идентичный документ, причина в том, что неизвестно, была ли пара ключ / значение атрибутом или
<key>value</key>, поэтому эта информация будет потеряна.Если вы спросите меня, атрибуты-это хак для начала; затем они снова хорошо работали для HTML.
Ну, вероятно, самый простой способ-это просто разобрать XML в словари, а затем сериализовать это с помощью simplejson.
хотя встроенные библиотеки для синтаксического анализа XML довольно хороши, я неравнодушен к lxml.
но для разбора RSS-каналов, я бы рекомендовал Универсальный Парсер Подачи, который также может анализировать Атом. Его главное преимущество заключается в том, что он может переваривать даже самые уродливые корма.
в Python 2.6 уже включает в себя парсер JSON, но новый версия с улучшенной скоростью доступно simplejson.
с этими инструментами здания ваше приложение не должно быть таким сложным.
Я бы предложил не идти на прямое преобразование. Преобразуйте XML в объект, а затем из объекта в JSON.
на мой взгляд, это дает более четкое определение того, как соответствуют XML и JSON.
требуется время, чтобы получить право, и вы даже можете написать инструменты, которые помогут вам с созданием некоторых из них, но это будет выглядеть примерно так:
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 ): ...
когда я делаю что-нибудь с XML в python, я почти всегда использую пакет lxml. Я подозреваю, что большинство людей используют lxml. Вы можете использовать xmltodict, но вам придется заплатить штраф за разбор XML снова.
для преобразования XML в json с помощью lxml вы:
- разбор XML-документа с помощью lxml
- конвертировать lxml в dict
- преобразовать список в json
Я использую следующий класс в своих проектах. Использовать метод 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)выход из встроенного главная - это:
{ "system": { "attributes": {}, "product": { "attributes": {}, "demod": { "attributes": {}, "frequency": { "attributes": { "units": "MHz", "value": "2.215" }, "blah": { "attributes": { "value": "1" } } } } } } }который является преобразованием этого 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
Если некоторое время вы получаете только код ответа вместо того, чтобы все данные затем ошибка, как JSON parse будет там, так что вам нужно преобразовать его как текст
import xmltodict data = requests.get(url) xpars = xmltodict.parse(data.text) json = json.dumps(xpars) print json
jsonpickle или если вы используете feedparser, вы можете попробовать feed_parser_to_json.py
Я нашел для простых XML-СНиПов, использование регулярного выражения сэкономит проблемы. Например:
# <user><name>Happy Man</name>...</user> import re names = re.findall(r'<name>(\w+)<\/name>', xml_string) # do some thing to namesчтобы сделать это путем синтаксического анализа XML, как сказал @Dan, нет единого для всех решения, потому что данные разные. Мое предложение-использовать lxml. Хотя и не закончил с json,lxml.объективировать дайте тихие хорошие результаты:
>>> 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'
мой ответ касается конкретного (и несколько распространенного) случая, когда вы на самом деле не нужно конвертировать весь xml в json, но вам нужно пройти / получить доступ к определенным частям xml, и вам нужно, чтобы это было быстро и простой (используя JSON / dict-подобные операции).
подход
для этого важно отметить, что разбор xml в etree с помощью
lxmlсупер быстро. Медленная часть в большинстве других ответы-это второй проход: прохождение структуры etree (обычно в python-land), преобразование ее в json.что приводит меня к подходу, который я нашел лучше всего для этого случая: разбор xml с помощью
lxml, а затем обертывание узлов etree (лениво), предоставляя им интерфейс, подобный dict.код
вот код:
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), )эта реализация не является полной, например, она не поддерживает чисто случаи, когда элемент имеет оба текста и атрибуты, или как текст, так и дети (только потому, что мне это не нужно, когда я его написал...) Это должно быть легко улучшить его, Хотя.
скорость
в моем конкретном случае использования, где мне нужно было только обрабатывать определенные элементы xml, этот подход дал удивительное и поразительное ускорение в 70 раз (!) по сравнению с помощью @Мартин Блех это xmltodict а затем пересечение диктанта непосредственно.
бонус
в качестве бонуса, так как наша структура уже диктует, мы получаем еще одну альтернативную реализацию
xml2jsonбесплатно. Нам просто нужно передать нашу дикт-подобную структуруjson.dumps. Что-то вроде:def xml_to_json(xmlstr, **kwargs): x = xml_to_dictlike(xmlstr, **kwargs) return json.dumps(x)если ваш xml включает атрибуты, вам нужно будет использовать некоторые буквенно-цифровые
attr_prefix(например, "ATTR_"), чтобы убедиться, что ключи являются действительными ключами json.Я не сравнивал эту часть.
этот материал здесь активно поддерживается и до сих пор является моим любимым: xml2json в python
проверьте lxml2json (раскрытие: я написал это)
https://github.com/rparelius/lxml2json
Это очень быстро, легкий (требуется только lxml), и одним из преимуществ является то, что у вас есть контроль над тем, преобразуются ли определенные элементы в списки или дикт
для представления данных в JSON
подготовка данных в Python: Чтобы создать JSON сначала вам нужно подготовить данные в python. Мы можем использовать список и словарь в Python для подготовки данных.
Python словарь JSON Объект (Формат Значения Ключа) Проверьте это для получения дополнительной информации подробности
https://devstudioonline.com/article/create-json-and-xml-in-python
Comments