Konwersja XML do JSON za pomocą Pythona?

Widziałem sporą część bezbożnego kodu XML - > JSON w sieci, i po interakcji z użytkownikami stosu przez chwilę, jestem przekonany,że ten tłum może pomóc więcej niż kilka pierwszych stron wyników Google.

Więc, analizujemy kanał pogody, i musimy wypełnić widżety pogody na wielu stronach internetowych. Obecnie szukamy rozwiązań opartych na Pythonie.

This public weather.com kanał RSS {[6] } jest dobrym przykładem tego, co będziemy analizować (nasz rzeczywisty weather.com feed zawiera dodatkowe informacje ze względu na partnerstwo w / them).

W skrócie, jak przekonwertować XML na JSON używając Pythona?

Author: dreftymac, 2008-10-10

19 answers

Nie ma mapowania "jeden do jednego" pomiędzy XML i JSON, więc konwersja jednego do drugiego wymaga pewnego zrozumienia tego, co chcesz zrobić z wynikami.

Mając to na uwadze, standardowa biblioteka Pythona zawiera kilka modułów do przetwarzania XML (w tym DOM, SAX i ElementTree). Począwszy od Pythona 2.6, wsparcie dla konwersji struktur danych Pythona do i z JSON jest zawarte w json Moduł .

Więc Infrastruktura jest tam.

 48
Author: Dan Lenski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-07 21:36:10

Xmltodict (pełne ujawnienie: napisałem go) może pomóc ci przekonwertować XML na strukturę dict+list+string, zgodnie z tym "standard" . Jest oparty na Expat , więc jest bardzo szybki i nie wymaga ładowania całego drzewa XML w pamięci.

Gdy już masz tę strukturę danych, możesz ją serializować do JSON:

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
 236
Author: Martin Blech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-18 01:06:05

Możesz użyć biblioteki xmljson do konwersji za pomocą różnych konwencji XML JSON.

Na przykład ten XML:

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

Przekłada się poprzez konwencję borsuków na to:

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

I poprzez konwencję GData do tego (atrybuty nie są obsługiwane):

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

... i za pomocą Konwencji Parkera do tego (atrybuty nie są obsługiwane):

{
  'p': 'text'
}

Jest możliwość konwersji z XML na JSON i z JSON na XML używając tego samego konwencje:

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

Ujawnienie: napisałem tę bibliotekę. Mam nadzieję, że pomoże to przyszłym poszukiwaczom.

 13
Author: S Anand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-20 07:37:54

Oto kod, który do tego zbudowałem. Nie ma parsowania zawartości, tylko zwykła konwersja.

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()
 6
Author: Paulo Vj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-19 15:35:21

Istnieje metoda transportowania znaczników opartych na XML jako JSON, która pozwala na bezstratną konwersję z powrotem do pierwotnej postaci. Zobacz http://jsonml.org/.

To rodzaj XSLT JSON. Mam nadzieję, że okaże się to pomocne

 5
Author: themihai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 09:18:02

Możesz rzucić okiem na http://designtheory.org/library/extrep/designdb-1.0.pdf . Ten projekt zaczyna się od konwersji XML do JSON dużej biblioteki plików XML. Przeprowadzono wiele badań nad konwersją i stworzono najprostsze, intuicyjne mapowanie XML - > JSON (zostało to opisane na początku dokumentu). Podsumowując, przekonwertuj wszystko na obiekt JSON i umieść powtarzające się bloki jako listę obiektów.

Obiekty oznaczające pary klucz / wartość (słownik w Python, hashmap w Javie, obiekt w JavaScript)

Nie ma mapowania z powrotem do XML, aby uzyskać identyczny dokument, powodem jest to, że nie wiadomo, czy para klucz / wartość była atrybutem, czy <key>value</key>, dlatego informacja ta jest tracona.

Jeśli O mnie chodzi, atrybuty są hackiem na początek; potem znowu działały dobrze dla HTML.

 4
Author: pykler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-31 20:28:15

Cóż, prawdopodobnie najprostszym sposobem jest po prostu parsowanie XML do słowników, a następnie serializowanie tego za pomocą simplejson.

 3
Author: dguaraglia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 14:30:59

Podczas gdy wbudowane biblioteki do parsowania XML są całkiem dobre, jestem częściowy do lxml.

Ale do parsowania kanałów RSS, polecam Universal Feed Parser , który może również parsować Atom. Jego główną zaletą jest to, że może trawić nawet najbardziej zniekształcone pasze.

Python 2.6 zawiera już parser JSON, ale nowsza wersja z ulepszoną prędkością{[2] } jest dostępna jako simplejson .

Z tymi narzędziami budowanie aplikacji nie powinno być takie trudne.

 2
Author: Luka Marinko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-31 20:25:18

Sugerowałbym nie iść na bezpośrednią konwersję. Konwertuje XML na obiekt, a następnie z obiektu na JSON.

Moim zdaniem daje to czystszą definicję tego, jak XML i JSON korespondują.

Potrzeba czasu, aby to zrobić i możesz nawet napisać narzędzia, które pomogą Ci wygenerować część z nich, ale wyglądałoby to mniej więcej tak:

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
Author: Michael Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-01 00:28:19

Kiedy robię cokolwiek z XML w Pythonie prawie zawsze używam pakietu lxml. Podejrzewam, że większość ludzi używa lxml. Możesz użyć xmltodict, ale będziesz musiał zapłacić karę za ponowne parsowanie XML.

Aby przekonwertować XML na json za pomocą lxml należy:

  1. Parse XML document with lxml
  2. Konwertuj lxml na dict
  3. Konwertuj listę na json

Używam następującej klasy w moich projektach. Użyj metody 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)

Wyjście z wbudowanego main to:

{
    "system": {
        "attributes": {}, 
        "product": {
            "attributes": {}, 
            "demod": {
                "attributes": {}, 
                "frequency": {
                    "attributes": {
                        "units": "MHz", 
                        "value": "2.215"
                    }, 
                    "blah": {
                        "attributes": {
                            "value": "1"
                        }
                    }
                }
            }
        }
    }
}

Który jest transformacją tego xml:

<system>
    <product>
        <demod>
            <frequency value='2.215' units='MHz'>
                <blah value='1'/>
            </frequency>
        </demod>
    </product>
</system>
 2
Author: shrewmouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-10 13:31:29

Do każdego, kto może jeszcze tego potrzebować. Oto nowszy, prosty kod do tej konwersji.

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
 2
Author: jnhustin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-06 18:06:24

Jeśli jakiś czas otrzymasz tylko Kod odpowiedzi zamiast wszystkich danych, wtedy Błąd jak JSON parse będzie tam więc musisz przekonwertować go jako tekst

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 
 2
Author: Akshay Kumbhar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-12 08:17:18

Jsonpickle lub jeśli używasz feedparsera, możesz spróbować feed_parser_to_json.py

 1
Author: choonkeat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 06:27:59

Znalazłem dla prostych wycinków XML, użycie wyrażenia regularnego zaoszczędzi kłopotów. Na przykład:

# <user><name>Happy Man</name>...</user>
import re
names = re.findall(r'<name>(\w+)<\/name>', xml_string)
# do some thing to names

Aby to zrobić przez parsowanie XML, jak powiedział @Dan, nie ma jednego dla wszystkich rozwiązania, ponieważ dane są różne. Moja propozycja to użycie lxml. Mimo, że nie został ukończony do json, lxml.objectify dają ciche dobre wyniki:

>>> 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
Author: Andrew_1510,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-05 15:59:42

Moja odpowiedź odnosi się do konkretnego (i dość częstego) przypadku, w którym tak naprawdę nie musisz przekonwertować całego xml {[9] } na json, ale musisz przejść / uzyskać dostęp do określonych części xml, i musisz to być szybki i prosty (używając operacji podobnych do json/dict).

Podejście

W tym celu należy zauważyć, że parsowanie xml do etree za pomocą lxml jest bardzo szybkie. Powolną częścią w większości innych odpowiedzi jest drugie przejście: przemierzanie struktury etree (zwykle w języku python-land), przekształcanie jej w json.

Co prowadzi mnie do podejścia, które uznałem za najlepsze w tym przypadku: parsowanie xml za pomocą lxml, a następnie owijanie węzłów etree (leniwie), zapewniając im interfejs podobny do dict.

Kod

Oto kod:

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

Ta implementacja nie jest kompletna, np. nie obsługuje czysto przypadków, w których element ma zarówno tekst, jak i atrybuty, lub zarówno tekst, jak i dzieci (tylko dlatego, że I nie potrzebowałem tego, kiedy to pisałem...) Powinno być jednak łatwo go poprawić.

Speed

W moim konkretnym przypadku użycia, gdzie musiałem przetwarzać tylko określone elementy xml, takie podejście dało zaskakujące i uderzające przyspieszenie o współczynnik 70 (!) w porównaniu z użyciem xmltodict @Martin Blech ' s i przejściem bezpośrednio do dict.

Bonus

Jako bonus, ponieważ nasza struktura jest już dict-like, otrzymujemy inną alternatywę implementacja xml2json za darmo. Musimy tylko przekazać naszą strukturę podobną do dict do json.dumps. Coś w stylu:

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

Jeśli twój xml zawiera atrybuty, musisz użyć jakiegoś alfanumerycznego attr_prefix (np. "ATTR_"), aby upewnić się, że klucze są poprawnymi kluczami json.

Nie analizowałem tej części.
 1
Author: shx2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-20 10:38:21

Ten materiał tutaj jest aktywnie utrzymywany i do tej pory jest moim ulubionym: xml2json w Pythonie

 1
Author: ifelsemonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-09 16:43:09

Sprawdź lxml2json (disclosure: I wrote it)

Https://github.com/rparelius/lxml2json

Jest bardzo szybki ,lekki( wymaga tylko lxml), a jedną z zalet jest to, że masz kontrolę nad tym, czy niektóre elementy są konwertowane do list lub dict

 0
Author: Robert Parelius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-12 01:21:51

Do reprezentowania danych w formacie JSON

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

W json powtarzamy dane w formacie klucza i wartości

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

Do reprezentowania danych w formacie 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-->
 0
Author: Anushree Anisha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 05:47:41

Przygotowanie danych w Python : Aby utworzyć JSON najpierw musisz przygotować dane w Pythonie. Do przygotowania danych możemy użyć List i słownika w Pythonie.

Python List JSON Array

Python słownik JSON Object (Format wartości klucza) Sprawdź to, aby uzyskać więcej informacji

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

 0
Author: Anushree Anisha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 06:08:47