Преобразование XML в JSON с использованием Python? - PullRequest
142 голосов
/ 10 октября 2008

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

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

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

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

Ответы [ 20 ]

2 голосов
/ 09 мая 2017

Когда я делаю что-нибудь с 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>
2 голосов
/ 04 апреля 2012

Я обнаружил, что для простых 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.objectify дает тихие хорошие результаты:

>>> 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'
1 голос
/ 30 ноября 2018

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

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

1 голос
/ 12 сентября 2018

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

https://github.com/rparelius/lxml2json

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

1 голос
/ 02 ноября 2017

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

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
1 голос
/ 09 февраля 2017

Этот материал активно поддерживается и пока мой любимый: xml2json in python

1 голос
/ 20 октября 2016

Мой ответ обращается к конкретному (и несколько распространенному) случаю, когда вам на самом деле не нужно преобразовывать весь xml в json, но вам нужно пройти / получить доступ к определенным частям xml, и вам нужно, чтобы оно было fast и simple (с использованием операций, похожих на 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 (!) случаев по сравнению с использованием @Martin Blech's 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.

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

1 голос
/ 09 декабря 2010

jsonpickle или если вы используете feedparser, вы можете попробовать feed_parser_to_json.py

0 голосов
/ 27 сентября 2018

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

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

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

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

0 голосов
/ 27 сентября 2018

Для представления данных в формате 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-->
...