Konversi XML ke JSON menggunakan Python?

170

Saya telah melihat bagian yang adil dari kode JSON XML -> fungainly di web, dan telah sedikit berinteraksi dengan pengguna Stack, saya yakin bahwa kerumunan ini dapat membantu lebih dari beberapa halaman pertama hasil Google.

Jadi, kami menguraikan umpan cuaca, dan kami perlu mengisi widget cuaca di banyak situs web. Kami sedang mencari solusi berbasis Python.

Umpan RSS weather.com publik ini adalah contoh yang baik dari apa yang akan kami uraikan (feed weather.com kami yang sebenarnya berisi informasi tambahan karena kemitraan dengan mereka ).

Singkatnya, bagaimana kita mengkonversi XML ke JSON menggunakan Python?

Pete Karl II
sumber

Jawaban:

61

Tidak ada pemetaan "satu-ke-satu" antara XML dan JSON, jadi mengonversi satu ke yang lain tentu memerlukan pemahaman tentang apa yang ingin Anda lakukan dengan hasilnya.

Yang sedang berkata, pustaka standar Python memiliki beberapa modul untuk parsing XML (termasuk DOM, SAX, dan ElementTree). Pada Python 2.6, dukungan untuk mengkonversi struktur data Python ke dan dari JSON termasuk dalam jsonmodul .

Jadi infrastrukturnya ada di sana.

Dan Lenski
sumber
2
xmljson IMHO adalah yang tercepat untuk digunakan dengan dukungan untuk berbagai konvensi di luar kotak. pypi.org/project/xmljson
nitinr708
Sudah disebutkan dalam jawaban yang lebih baru. Itu masih hanya mencakup sebagian kecil konstruksi XML yang valid, tetapi mungkin sebagian besar yang digunakan orang dalam praktik.
Dan Lenski
281

xmltodict (pengungkapan penuh: saya menulisnya) dapat membantu Anda mengonversi XML Anda menjadi struktur daftar + string + dict, mengikuti "standar" ini . Itu adalah Expat berbasis, sehingga sangat cepat dan tidak perlu memuat pohon XML keseluruhan dalam memori.

Setelah Anda memiliki struktur data itu, Anda bisa membuat cerita bersambung menjadi JSON:

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
Martin Blech
sumber
@ Martin Blech Jika saya membuat file json dari file model Django saya. Bagaimana saya bisa memetakan file xml saya untuk mengkonversi xml ke json untuk bidang yang diperlukan?
katakanlah
1
@ mengatakan saya pikir Anda harus memposting ini sebagai pertanyaan SO terpisah.
Martin Blech
@Martin Blech. Saya menambahkan pertanyaan, tetapi agak sulit untuk menyesuaikannya dengan SO, saya seorang pemula jadi telah memberikan info sebanyak yang saya bisa, tetapi saya berharap Anda mungkin memerlukan lebih banyak kejelasan stackoverflow.com/q/23676973/461887
sayth
Setelah sekian lama, saya agak terkejut xmltodict bukan perpustakaan "standar" di beberapa distribusi linux. Meskipun tampaknya melakukan pekerjaan langsung dari apa yang bisa kita baca, sayangnya saya akan menggunakan solusi lain seperti konversi xslt
sancelot
Terima kasih banyak untuk menulis perpustakaan yang fantastis ini. Meskipun bs4dapat melakukan pekerjaan xml untuk mendiktkan, sangat mudah untuk menggunakan perpustakaan
Tessaracter
24

Anda bisa menggunakan pustaka xmljson untuk mengonversi menggunakan konvensi XML JSON yang berbeda .

Misalnya, XML ini:

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

diterjemahkan melalui konvensi BadgerFish ke dalam ini:

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

dan melalui konvensi GData ke ini (atribut tidak didukung):

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

... dan melalui konvensi Parker ke dalam ini (atribut tidak didukung):

{
  'p': 'text'
}

Dimungkinkan untuk mengonversi dari XML ke JSON dan dari JSON ke XML menggunakan konvensi yang sama:

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

Pengungkapan: Saya menulis perpustakaan ini. Semoga ini membantu pencari di masa depan.

S Anand
sumber
4
Itu perpustakaan yang sangat keren, tapi tolong baca Bagaimana cara menawarkan perpustakaan open-source pribadi? sebelum Anda memposting lebih banyak jawaban, pamerkan saja.
Martijn Pieters
1
Terima kasih @MartijnPieters - Saya baru saja melewati ini dan akan memastikan saya tetap pada ini.
S Anand
1
Terima kasih Anand untuk solusinya - tampaknya berfungsi dengan baik, tidak memiliki dependensi eksternal, dan menyediakan banyak fleksibilitas dalam bagaimana atribut ditangani menggunakan konvensi yang berbeda. Persis apa yang saya butuhkan dan merupakan solusi paling fleksibel dan paling sederhana yang saya temukan.
mbbeme
Terima kasih Anand - sayangnya, saya tidak dapat mem-parsing XML dengan utf8 encoding. Pergi melalui sumber, tampaknya set pengkodean melalui XMLParser (..) diabaikan
Patrik Beck
@ PatrikBeck, bisakah Anda membagikan contoh kecil XML dengan pengkodean utf8 yang rusak?
S Anand
11

Jika suatu saat Anda hanya mendapatkan kode respons alih-alih semua data maka kesalahan seperti json parse akan ada di sana sehingga Anda harus mengubahnya sebagai teks

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 
Akshay Kumbhar
sumber
7

Ini kode yang saya buat untuk itu. Tidak ada penguraian konten, hanya konversi biasa.

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()
Paulo Vj
sumber
7

Ada metode untuk mengangkut markup berbasis XML sebagai JSON yang memungkinkannya dikonversi kembali ke bentuk aslinya. Lihat http://jsonml.org/ .

Ini semacam XSLT dari JSON. Saya harap Anda merasa terbantu

themihai
sumber
7

Untuk siapa saja yang mungkin masih membutuhkan ini. Ini kode baru yang lebih sederhana untuk melakukan konversi ini.

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
jhhustin
sumber
1
Fungsinya setidaknya dalam Python 3.7, meskipun sayangnya ia menambahkan beberapa data tak terduga ke nama-nama kunci jika nilai-nilai tertentu ada di xml Anda, misalnya tag xmlns pada simpul level akar muncul di setiap kunci simpul seperti ini: {'{ maven .apache.org / POM / 4.0.0 } artifactId ':' test-service ', yang berasal dari xml seperti ini: <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
5

Anda mungkin ingin melihat http://designtheory.org/library/extrep/designdb-1.0.pdf . Proyek ini dimulai dengan konversi XML ke JSON dari perpustakaan besar file XML. Ada banyak penelitian yang dilakukan dalam konversi, dan XML -> pemetaan JSON intuitif paling sederhana diproduksi (dijelaskan di awal dokumen). Singkatnya, konversikan semuanya menjadi objek JSON, dan letakkan blok berulang sebagai daftar objek.

objek yang berarti pasangan kunci / nilai (kamus dengan Python, hashmap di Jawa, objek dalam JavaScript)

Tidak ada pemetaan kembali ke XML untuk mendapatkan dokumen yang identik, alasannya, tidak diketahui apakah pasangan kunci / nilai adalah atribut atau <key>value</key> , oleh karena itu informasi tersebut hilang.

Jika Anda bertanya kepada saya, atribut adalah retas untuk memulai; sekali lagi mereka bekerja dengan baik untuk HTML.

pengadu
sumber
4

Yah, mungkin cara paling sederhana adalah parsing XML ke dalam kamus dan kemudian serialkan dengan simplejson.

dguaraglia
sumber
4

Saya sarankan tidak pergi untuk konversi langsung. Konversi XML ke objek, lalu dari objek ke JSON.

Menurut pendapat saya, ini memberikan definisi yang lebih bersih tentang bagaimana XML dan JSON sesuai.

Butuh waktu untuk memperbaiki dan Anda bahkan dapat menulis alat untuk membantu Anda menghasilkan beberapa, tetapi akan terlihat seperti ini:

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 ):
    ...
Michael Anderson
sumber
2

Saya menemukan untuk snips XML sederhana, menggunakan ekspresi reguler akan menghemat masalah. Sebagai contoh:

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

Untuk melakukannya dengan parsing XML, seperti yang dikatakan @Dan, tidak ada solusi satu-untuk-semua karena datanya berbeda. Saran saya adalah menggunakan lxml. Meskipun belum selesai untuk json, lxml.objectify memberikan hasil yang bagus dan tenang:

>>> 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
sumber
1
tetapi menghapus duplikat node
Pooya
2

Sementara built-in libs untuk parsing XML cukup bagus, saya sebagian ke lxml .

Tetapi untuk penguraian RSS feed, saya akan merekomendasikan Universal Feed Parser , yang juga dapat mengurai Atom. Keuntungan utamanya adalah dapat mencerna bahkan sebagian besar feed yang cacat.

Python 2.6 sudah menyertakan parser JSON, tetapi versi yang lebih baru dengan peningkatan kecepatan tersedia sebagai simplejson .

Dengan alat-alat ini membangun aplikasi Anda seharusnya tidak terlalu sulit.

Luka Marinko
sumber
2

Jawaban saya membahas kasus spesifik (dan agak umum) di mana Anda tidak benar-benar perlu mengubah seluruh xml menjadi json, tetapi yang Anda butuhkan adalah untuk melintasi / mengakses bagian-bagian tertentu dari xml, dan Anda perlu cepat , dan sederhana (menggunakan operasi json / dict-like).

Pendekatan

Untuk ini, penting untuk dicatat bahwa parsing xml untuk menggunakan etree lxmlsangat cepat. Bagian lambat di sebagian besar jawaban lain adalah lintasan kedua: melintasi struktur etree (biasanya di python-land), mengubahnya menjadi json.

Yang membawa saya ke pendekatan yang saya temukan terbaik untuk kasus ini: parsing menggunakan xml lxml, dan kemudian membungkus node etree (malas), menyediakan mereka dengan antarmuka seperti dict.

Kode

Berikut kodenya:

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

Implementasi ini tidak lengkap, misalnya, tidak mendukung kasus di mana sebuah elemen memiliki teks dan atribut, atau teks dan anak-anak (hanya karena saya tidak membutuhkannya ketika saya menulisnya ...) Itu harus mudah untuk memperbaikinya.

Mempercepat

Dalam kasus penggunaan khusus saya, di mana saya hanya perlu memproses elemen-elemen spesifik xml, pendekatan ini memberikan speedup mengejutkan dan mencolok dengan faktor 70 (!) Dibandingkan dengan menggunakan xmltodict @Martin Blech dan kemudian menelusuri dict secara langsung.

Bonus

Sebagai bonus, karena struktur kami sudah seperti dict, kami mendapatkan implementasi alternatif lain xml2jsonsecara gratis. Kita hanya perlu meneruskan struktur seperti dict kita json.dumps. Sesuatu seperti:

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

Jika xml Anda menyertakan atribut, Anda harus menggunakan beberapa alfanumerik attr_prefix (mis. "ATTR_"), untuk memastikan kunci tersebut adalah kunci json yang valid.

Saya belum membandingkan bagian ini.

shx2
sumber
Jika saya coba lakukan json.dumps(tree)itu mengatakan Objek ketik 'ETreeDictWrapper' bukan JSON serializable
Vlad T.
2

Ketika saya melakukan sesuatu dengan XML di python saya hampir selalu menggunakan paket lxml. Saya curiga kebanyakan orang menggunakan lxml. Anda dapat menggunakan xmltodict tetapi Anda harus membayar penalti untuk mem-parsing XML lagi.

Untuk mengonversi XML ke json dengan lxml Anda:

  1. Parsing dokumen XML dengan lxml
  2. Ubah lxml menjadi dict
  3. Konversi daftar ke json

Saya menggunakan kelas berikut dalam proyek saya. Gunakan metode 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)

Output dari main built in adalah:

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

Yang merupakan transformasi dari xml ini:

<system>
    <product>
        <demod>
            <frequency value='2.215' units='MHz'>
                <blah value='1'/>
            </frequency>
        </demod>
    </product>
</system>
shrewmouse
sumber
1

Barang-barang ini di sini dipelihara secara aktif dan sejauh ini adalah favorit saya: xml2json in python

kebenaranadjustr
sumber
1

lihat lxml2json (pengungkapan: Saya menulisnya)

https://github.com/rparelius/lxml2json

itu sangat cepat, ringan (hanya membutuhkan lxml), dan satu keuntungan adalah Anda memiliki kontrol atas apakah elemen tertentu dikonversi ke daftar atau dikte

Robert Parelius
sumber
1

Anda dapat menggunakan declxml. Ini memiliki fitur-fitur canggih seperti multi atribut dan dukungan bersarang yang kompleks. Anda hanya perlu menulis prosesor sederhana untuk itu. Juga dengan kode yang sama, Anda dapat mengonversi kembali ke JSON juga. Ini cukup mudah dan dokumentasinya luar biasa.

Tautan: https://declxml.readthedocs.io/en/latest/index.html

srth12
sumber
-1

Mempersiapkan data dengan Python : Untuk membuat JSON, Anda harus menyiapkan data dengan python terlebih dahulu. Kita bisa menggunakan Daftar dan Kamus dengan Python untuk menyiapkan data.

Daftar Python <==> Array JSON

Kamus Python <==> Objek JSON (Format Nilai Kunci) Periksa ini untuk lebih jelasnya

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

Anushree Anisha
sumber
Selamat Datang di Stack Overflow! Meskipun tautan adalah cara yang bagus untuk berbagi pengetahuan, mereka tidak akan benar-benar menjawab pertanyaan jika mereka rusak di masa depan. Tambahkan ke jawaban Anda konten penting dari tautan yang menjawab pertanyaan. Jika kontennya terlalu kompleks atau terlalu besar untuk muat di sini, jelaskan gagasan umum dari solusi yang diajukan. Ingatlah untuk selalu menyimpan referensi tautan ke situs web solusi asli. Lihat: Bagaimana saya menulis jawaban yang baik?
sɐunıɔ ןɐ qɐp
-4

Untuk mewakili data dalam format JSON

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

Di json kita merepresentasikan data dalam format kunci dan nilai

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

Untuk mewakili data dalam format 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-->

Anushree Anisha
sumber