Перетворення XML в JSON за допомогою Python?


170

Я бачив неабияку частку коду XML-> JSON в Інтернеті і трохи взаємодіяв із користувачами Stack, я переконаний, що ця натовп може допомогти більше, ніж перші кілька сторінок результатів Google.

Отже, ми розбираємо погоду, і нам потрібно заповнити віджети погоди на безлічі веб-сайтів. Зараз ми розглядаємо рішення на основі Python.

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

Якщо коротко, то як перетворити XML в JSON за допомогою Python?

Відповіді:


61

Немає «однозначного» відображення між XML та JSON, тому перетворення одного в інше обов’язково вимагає певного розуміння того, що ви хочете зробити з результатами.

При цьому, стандартна бібліотека Python має кілька модулів для розбору XML (включаючи DOM, SAX та ElementTree). Станом на Python 2.6, підтримка перетворення структур даних Python в JSON і з нього включена в 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 Я думаю, ви повинні опублікувати це як окреме питання.
Мартін Блех

@Martin Blech. Я додав запитання, але це досить важко вписатись в SO, я початківець, тому я надав стільки інформації, скільки можу, але, напевно, вам знадобиться більше ясності stackoverflow.com/q/23676973/461887
кажуть,

Після такого часу я трохи здивований xmltodict - це не "стандартна" бібліотека в деяких дистрибутивах Linux. Хоча, здається,
виконую

Дякую тонну за написання цієї фантастичної бібліотеки. Хоча bs4можна виконати роботу з xml, щоб
диктувати,

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
Це досить класна бібліотека, але прочитайте, як запропонувати персональні бібліотеки з відкритим кодом? перед тим, як опублікувати більше відповідей, демонструючи це.
Martijn Pieters

1
Дякую @MartijnPieters - Я щойно пережив це і переконаюся, що я дотримуюся цього.
S Anand

1
Дякуємо Ананд за рішення - воно, здається, працює добре, не має зовнішніх залежностей і забезпечує велику гнучкість в управлінні атрибутами за допомогою різних умов. Саме те, що мені було потрібно, було найбільш гнучким і найпростішим рішенням, яке я знайшов.
mbbeme

Дякую Ананд - на жаль, я не можу змусити його проаналізувати XML з кодуванням utf8. Переходячи через джерела, схоже, що набір кодування через XMLParser (..) ігнорується
Патрік Бек

@PatrikBeck, будь ласка, поділіться невеликим прикладом XML з кодування utf8, яке порушується?
S Anand

11

Якщо через деякий час ви отримаєте лише код відповіді замість усіх даних, тоді буде помилка, як json синтаксичний розбір , тому вам потрібно перетворити його як текст

import xmltodict

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

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

7

Існує метод транспортування розмітки на основі XML як JSON, який дозволяє без втрат перетворюватися назад у початковий вигляд. Дивіться http://jsonml.org/ .

Це свого роду XSLT JSON. Я сподіваюся, що Ви вважаєте це корисним


7

Для всіх, хто може все-таки цього потребувати. Ось новий, простий код для цього перетворення.

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
Він функціонує як мінімум у 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-in substance "> <modelVersion> 4.0.0 </ modelVersion>
hrbdg

5

Ви можете подивитися http://designtheory.org/library/extrep/designdb-1.0.pdf . Цей проект починається з перетворення XML в JSON великої бібліотеки XML-файлів. Було проведено багато досліджень при перетворенні, і було виготовлено найпростіше інтуїтивно зрозуміле XML -> JSON-відображення (це описано на початку документа). Підсумовуючи, конвертуйте все в об'єкт JSON і додайте повторювані блоки як список об'єктів.

об'єкти, що означають пари ключів / значень (словник на Python, хешмап на Java, об'єкт у JavaScript)

Немає відображення назад у XML, щоб отримати ідентичний документ, причина - невідомо, пара чи ключ / значення була атрибутом чи <key>value</key> , тому інформація втрачається.

Якщо ви запитаєте мене, атрибути - це хакер для початку; потім знову добре працювали для HTML.


4

Ну, мабуть, найпростіший спосіб - просто проаналізувати XML у словниках, а потім серіалізувати це за допомогою simplejson.


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. Хоча не завершено до 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
але він видаляє повторювані вузли
Pooya

2

Хоча вбудовані лібри для розбору XML дуже гарні, я частковий до lxml .

Але для розбору RSS-каналів я рекомендую Universal Feed Parser , який також може аналізувати Atom. Його головна перевага полягає в тому, що він може перетравлювати навіть більшість неправильних кормів.

Python 2.6 вже включає аналізатор JSON, але новіша версія з покращеною швидкістю доступна як simplejson .

За допомогою цих інструментів створення вашої програми не повинно бути таким складним.


2

Моя відповідь стосується конкретного (і дещо поширеного) випадку, коли вам не потрібно конвертувати весь xml в json, але вам потрібно перейти / отримати доступ до конкретних частин xml, і вам потрібно, щоб він був швидким , і простий (з використанням json / dict-подібних операцій).

Підхід

Для цього важливо відзначити, що синтаксичний аналіз xml до etree використовується lxmlдуже швидко. Повільна частина більшості інших відповідей - це другий перехід: проходження структури етрі (зазвичай в python-land), перетворення її в json.

Що призводить мене до підходу, який я знайшов найкращим для цього випадку: розбору xml за допомогою lxml , а потім загортання вузлів etree (ліниво), надання їм інтерфейсу, що нагадує дікт.

Код

Ось код:

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.

Я не оцінював цю частину.


Якщо я спробую json.dumps(tree)це зробити, він каже, що " Об'єкт типу" ETreeDictWrapper "- це не серіалізація JSON
Влад Т.

2

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

Щоб перетворити XML в json за допомогою lxml, виконайте вказані нижче дії.

  1. Розбір XML-документа з lxml
  2. Перетворити lxml в дікт
  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


-1

Підготуйте дані в Python : Для створення JSON спочатку потрібно підготувати дані в python. Ми можемо використовувати Список і Словник на Python для підготовки даних.

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

Python Dictionary <==> JSON Object (Key Value Format) Перевірте це для отримання більш детальної інформації

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.