Преобразование XML в JSON с использованием Python?

170

Я видел довольно много неуклюжего кода XML-> JSON в Интернете, и, немного пообщавшись с пользователями Stack, я убежден, что эта толпа может помочь больше, чем первые несколько страниц результатов Google.

Итак, мы разбираем фид погоды, и нам нужно заполнить виджеты погоды на множестве веб-сайтов. Сейчас мы ищем решения на основе Python.

Этот общедоступный RSS-канал weather.com является хорошим примером того, что мы будем анализировать ( наш реальный канал weather.com содержит дополнительную информацию благодаря партнерству с ними ).

В двух словах, как мы должны конвертировать XML в JSON, используя Python?

Пит Карл II
источник

Ответы:

61

Между XML и JSON нет сопоставления «один к одному», поэтому преобразование одного в другое обязательно требует некоторого понимания того, что вы хотите сделать с результатами.

При этом стандартная библиотека Python имеет несколько модулей для анализа XML (включая DOM, SAX и ElementTree). Начиная с Python 2.6, в jsonмодуль включена поддержка преобразования структур данных Python в JSON и из него .

Итак, инфраструктура есть.

Дэн Ленски
источник
2
xmljson IMHO является самым быстрым в использовании с поддержкой различных соглашений из коробки. pypi.org/project/xmljson
nitinr708
Это уже упоминалось в новых ответах. Он по-прежнему охватывает лишь небольшое подмножество допустимых конструкций XML, но, вероятно, большинство из того, что люди используют на практике.
Дэн Ленски
281

xmltodict (полное раскрытие: я написал) может помочь вам преобразовать ваш XML в структуру dict + list + string, следуя этому «стандарту» . Это Экспат на , поэтому он очень быстрый и не требует загрузки всего дерева XML в память.

Получив эту структуру данных, вы можете сериализовать ее в JSON:

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
Мартин Блех
источник
@Martin Blech Если я создаю файл json из моего файла моделей django. Как я могу сопоставить мой файл XML для преобразования XML в JSON для обязательных полей?
скажем
1
@sayth Я думаю, вы должны опубликовать это как отдельный вопрос SO.
Мартин Блех
@ Мартин Блех. Я добавил вопрос, но в SO довольно сложно вписаться, я новичок, поэтому предоставил как можно больше информации, но я ожидаю, что вам может потребоваться больше ясности stackoverflow.com/q/23676973/461887
скажем,
После столь длительного времени я немного удивлен, что xmltodict не является «стандартной» библиотекой в ​​некоторых дистрибутивах Linux. Несмотря на то, что кажется, что мы делаем работу прямо из того, что мы можем прочитать, я, к сожалению, буду использовать другое решение, такое как преобразование xslt
sancelot
Огромное спасибо за написание этой фантастической библиотеки. Хотя bs4может сделать работу XML в Словаре чрезвычайно прост в использовании библиотеки
Tessaracter
24

Вы можете использовать библиотеку xmljson для преобразования с использованием различных соглашений XML JSON .

Например, этот XML:

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

переводится через соглашение BadgerFish в это:

{
  'p': {
    '@id': 1,
    '$': 'text'
  }
}

и через соглашение GData в это (атрибуты не поддерживаются):

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

... и через соглашение Паркера в это (атрибуты не поддерживаются):

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

Раскрытие: я написал эту библиотеку. Надеюсь, это поможет будущим искателям.

С Ананд
источник
4
Это довольно крутая библиотека, но, пожалуйста, прочитайте Как предложить персональные библиотеки с открытым исходным кодом? прежде чем отправлять больше ответов, демонстрируя это.
Мартин Питерс
1
Спасибо @MartijnPieters - я только что прошел через это и буду следить за этим.
Ан
1
Спасибо Ананду за решение - похоже, оно работает хорошо, не имеет внешних зависимостей и обеспечивает большую гибкость в том, как обрабатываются атрибуты с использованием различных соглашений. Именно то, что мне было нужно, и было самым гибким и простым решением, которое я нашел.
mbbeme
Спасибо, Ананд, к сожалению, я не могу разобрать XML с кодировкой utf8. По источникам, кажется, что кодировка, заданная через XMLParser (..), игнорируется
Патрик Бек,
@PatrikBeck Не могли бы вы поделиться небольшим примером XML с нарушением кодировки utf8?
С Ананд
11

Если через какое-то время вы получите только код ответа вместо всех данных, то появится ошибка типа json parse , поэтому вам нужно преобразовать ее в текст

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 
Акшай Кумбхар
источник
8

Для тех, кто еще может нуждаться в этом. Вот новый, простой код для этого преобразования.

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
jnhustin
источник
1
По крайней мере, он работает в Python 3.7, но, к сожалению, добавляет некоторые неожиданные данные к именам ключей, если в вашем xml-файле есть определенные значения, например, тег xmlns на узле корневого уровня отображается в каждом ключе узла следующим образом: {'{ maven .apache.org / POM / 4.0.0 } artifactId ':' test-service ', пришедший из xml следующим образом: <project xmlns = " maven.apache.org/POM/4.0.0 " xsi: schemaLocation = " maven .apache.org / POM / 4.0.0 maven.apache.org/xsd/maven-4.0.0.xsd "xmlns: xsi =" w3.org/2001/XMLSchema-instance "> <modelVersion> 4.0.0 </ modelVersion>
hrbdg
7

Вот код, который я построил для этого. Там нет разбора содержимого, просто преобразование.

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()
Пауло Vj
источник
7

Существует метод для транспортировки разметки на основе XML в виде JSON, который позволяет преобразовать ее без потерь обратно в исходную форму. Смотрите http://jsonml.org/ .

Это своего рода XSLT из JSON. Я надеюсь, что вы найдете это полезным

themihai
источник
5

Возможно, вы захотите взглянуть на http://designtheory.org/library/extrep/designdb-1.0.pdf . Этот проект начинается с преобразования XML в JSON большой библиотеки файлов XML. Было проведено много исследований в области преобразования, и было создано самое простое интуитивно понятное отображение XML -> JSON (оно описано в начале документа). Таким образом, преобразуйте все в объект JSON и поместите повторяющиеся блоки в список объектов.

объекты, означающие пары ключ / значение (словарь в Python, hashmap в Java, объект в JavaScript)

Нет возврата к XML для получения идентичного документа, причина в том, что неизвестно, была ли пара ключ / значение атрибутом или <key>value</key>, следовательно, эта информация потеряна.

Если вы спросите меня, атрибуты - это взлом для начала; с другой стороны, они хорошо работали для HTML.

pykler
источник
4

Ну, наверное, самый простой способ - просто разобрать XML в словари и затем сериализовать это с simplejson.

dguaraglia
источник
4

Я бы предложил не идти на прямое обращение. Преобразуйте 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 ):
    ...
Майкл Андерсон
источник
2

Я нашел для простых фрагментов 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. Lxml.objectify, хотя и не завершено для json, дает хорошие результаты:

>>> 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'
Andrew_1510
источник
1
но он удаляет дубликаты узлов
Pooya
2

Хотя встроенные библиотеки для разбора XML довольно хороши, я неравнодушен к lxml .

Но для разбора RSS-каналов я бы порекомендовал Universal Feed Parser , который также может анализировать Atom. Его главное преимущество заключается в том, что он может переваривать даже самые плохо сформированные корма.

Python 2.6 уже включает в себя парсер JSON, но более новая версия с улучшенной скоростью доступна как simplejson .

С этими инструментами создание вашего приложения не должно быть таким сложным.

Лука Маринко
источник
2

Мой ответ касается конкретного (и несколько распространенного) случая, когда вам на самом деле не нужно конвертировать весь 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 @Martin Blech и последующим обходом диктата напрямую.

бонус

В качестве бонуса, поскольку наша структура уже похожа на диктовку, мы получаем другую альтернативную реализацию xml2jsonбесплатно. Нам просто нужно передать нашу подобную диктату структуру json.dumps. Что-то вроде:

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

Если ваш xml содержит атрибуты, вам нужно использовать буквенно-цифровые attr_prefix символы (например, «ATTR_»), чтобы ключи были действительными ключами json.

Я не тестировал эту часть.

shx2
источник
Если я попытаюсь json.dumps(tree)это сделать, то объект типа «ETreeDictWrapper» не будет сериализуем в формате JSON
Влад Т.
2

Когда я делаю что-нибудь с XML в python, я почти всегда использую пакет lxml. Я подозреваю, что большинство людей используют lxml. Вы можете использовать xmltodict, но вам придется заплатить штраф за повторный анализ XML.

Чтобы преобразовать XML в JSON с помощью LXML вы:

  1. Разбор XML-документа с помощью lxml
  2. Конвертировать lxml в dict
  3. Конвертировать список в 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>
землеройка
источник
1

проверить lxml2json (раскрытие: я написал это)

https://github.com/rparelius/lxml2json

это очень быстро, легко (требуется только lxml), и одним из преимуществ является то, что вы можете контролировать, преобразуются ли определенные элементы в списки или в диктовки

Роберт Парелиус
источник
1

Вы можете использовать declxml. Он имеет расширенные функции, такие как множественные атрибуты и сложную вложенную поддержку. Вам просто нужно написать простой процессор для него. Также с тем же кодом вы можете конвертировать обратно в JSON. Это довольно просто и документация потрясающая.

Ссылка: https://declxml.readthedocs.io/en/latest/index.html

srth12
источник
-1

Подготовка данных в Python : Для создания JSON сначала вам необходимо подготовить данные в Python. Мы можем использовать List и Dictionary в Python для подготовки данных.

Список Python <==> Массив JSON

Словарь Python <==> Объект JSON (формат значения ключа) Проверьте это для более подробной информации

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

Анушри Аниша
источник
Добро пожаловать в стек переполнения! Хотя ссылки являются отличным способом обмена знаниями, они не будут действительно отвечать на вопрос, если они будут нарушены в будущем. Добавьте к своему ответу основное содержание ссылки, которая отвечает на вопрос. Если содержание слишком сложное или слишком большое, чтобы поместиться здесь, опишите общую идею предложенного решения. Не забывайте всегда сохранять ссылку на сайт оригинального решения. Смотрите: Как мне написать хороший ответ?
sɐunıɔ ןɐ qɐp
-4

Представлять данные в формате JSON

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

В json мы представляем данные в формате ключ и значение

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

Представлять данные в формате 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-->

Анушри Аниша
источник