XML'yi Python kullanarak JSON'a dönüştürme?


171

Web'de ungainly XML-> JSON kodunun adil bir payını gördüm ve Stack kullanıcılarıyla bir süre etkileşim kurdum, bu kalabalığın Google sonuçlarının ilk birkaç sayfasından daha fazla yardımcı olabileceğine ikna oldum.

Dolayısıyla, bir hava durumu özet akışını ayrıştırıyoruz ve hava durumu widget'larını çok sayıda web sitesinde doldurmamız gerekiyor. Şimdi Python tabanlı çözümlere bakıyoruz.

Bu genel weather.com RSS feed'i , ayrıştıracağımızın iyi bir örneğidir ( gerçek weather.com feed'imiz, kendileriyle olan bir ortaklık nedeniyle ek bilgiler içerir ).

Özetle, XML'i Python kullanarak JSON'a nasıl dönüştürmeliyiz?

Yanıtlar:


61

XML ve JSON arasında "bire bir" eşleme yoktur, bu nedenle birini diğerine dönüştürmek , sonuçlarla ne yapmak istediğinizi biraz anlamayı gerektirir .

Bununla birlikte, Python'un standart kütüphanesinde XML ayrıştırma için birkaç modül vardır (DOM, SAX ve ElementTree dahil). Python 2.6 itibariyle, Python veri yapılarını JSON'a ve JSON'dan dönüştürme desteği jsonmodüle dahildir .

Yani altyapı orada.


2
xmljson IMHO, kutudan çıkan çeşitli konvansiyonlar için destekle en hızlı kullanımdır. pypi.org/project/xmljson
nitinr708

Daha yeni cevaplarda zaten bahsedildi. Hala geçerli XML yapılarının küçük bir alt kümesini, ancak muhtemelen insanların pratikte kullandıklarının çoğunu kapsar.
Dan Lenski

282

xmltodict (tam açıklama: yazdım) XML'inizi bu "standardı" izleyerek bir dict + list + string yapısına dönüştürmenize yardımcı olabilir . Öyle Expat yüzden çok hızlı ve hafızada bütün XML ağaç yüklemek gerekmez, tabanlı.

Bu veri yapısına sahip olduğunuzda, bunu JSON'a serileştirebilirsiniz:

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'

@Martin Blech Django modellerim dosyasından bir json dosyası oluşturursam. Gerekli alanları için xml'yi json'a dönüştürmek için xml dosyamı nasıl eşleyebilirim?
sayth

1
@sayth Bunu ayrı bir SO sorusu olarak yayınlamanız gerektiğini düşünüyorum.
Martin Blech

@Martin Blech. Bir soru ekledim, ancak
SO'ya

Bu kadar uzun zaman sonra, xmltodict bazı linux dağıtımlarında bir "standart" kütüphane değil biraz şaşırdım. Bu işi doğrudan okuyabildiğimizden yapıyor gibi görünse de, ne yazık ki xslt dönüşümü gibi başka bir çözüm kullanacağım
sancelot

Bu muhteşem kütüphaneyi yazdığınız için bir ton teşekkürler. Gerçi bs4xml işini yapabilmek için kütüphane kullanmak son derece kolaydır
Tessaracter

24

Farklı XML JSON kurallarını kullanarak dönüştürmek için xmljson kütüphanesini kullanabilirsiniz .

Örneğin, bu XML:

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

BadgerFish sözleşmesi aracılığıyla bunu çevirir :

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

ve buna GData kuralı aracılığıyla (özellikler desteklenmez):

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

... ve Parker sözleşmesi yoluyla (özellikler desteklenmez):

{
  'p': 'text'
}

Aynı kuralları kullanarak XML'den JSON'a ve JSON'dan XML'e dönüştürmek mümkündür:

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

Açıklama: Bu kütüphaneyi yazdım. Umarım gelecekteki araştırmacılara yardımcı olur.


4
Bu oldukça havalı bir kütüphane, ancak lütfen Kişisel açık kaynaklı kütüphaneler nasıl sunulur? daha fazla cevap göndermeden önce.
Martijn Pieters

1
Teşekkürler @MartijnPieters - Sadece bunun üzerinden geçtim ve buna sadık kalacağımdan emin olacağım.
S Anand

1
Çözüm için teşekkürler Anand - iyi çalışıyor gibi görünüyor, dış bağımlılıkları yok ve niteliklerin farklı kurallar kullanılarak nasıl ele alınacağı konusunda çok fazla esneklik sağlıyor. Tam olarak ihtiyacım olan şey ve bulduğum en esnek ve en basit çözümdü.
mbbeme

Teşekkürler Anand - ne yazık ki, utf8 kodlaması ile XML ayrıştırmak için alamıyorum. Kaynaklara gittikçe, XMLParser (..) üzerinden kodlama seti göz ardı edildi
Patrik Beck

@PatrikBeck, kopan utf8 kodlamasıyla küçük bir XML örneği paylaşabilir misiniz?
S Anand

11

Bir süre tüm veriler yerine sadece yanıt kodu alırsanız json ayrıştırma gibi hata orada olacak, bu yüzden u metin olarak dönüştürmeniz gerekiyor

import xmltodict

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

8

Buna ihtiyaç duyan herkese. İşte bu dönüşümü yapmak için daha yeni, basit bir kod.

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
En azından Python 3.7'de çalışır, ancak maalesef xml'nizde belirli değerler varsa anahtar adlarına beklenmedik veriler ekler, örneğin kök düğümü düğümünde bir xmlns etiketi her düğüm anahtarında şöyle görünür: {'{ maven .apache.org / POM / 4.0.0 } artifactId ':' test-service ', xml'den şu şekilde geldi: <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

İşte bunun için inşa ettiğim kod. İçeriği ayrıştırma yok, sadece düz dönüştürme.

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 tabanlı işaretlemeyi JSON olarak taşımak için kayıpsız bir şekilde orijinal biçimine dönüştürülmesini sağlayan bir yöntem vardır. Bkz. Http://jsonml.org/ .

Bu bir tür XSLT JSON. Umarım faydalı bulursun


5

Http://designtheory.org/library/extrep/designdb-1.0.pdf adresine göz atmak isteyebilirsiniz . Bu proje, XML dosyalarının büyük bir kütüphanesinin XML'den JSON'a dönüştürülmesiyle başlar. Dönüştürmede çok fazla araştırma yapıldı ve en basit sezgisel XML -> JSON eşlemesi üretildi (belgenin başlarında açıklanmıştır). Özetle, her şeyi bir JSON nesnesine dönüştürün ve yinelenen blokları bir nesne listesi olarak yerleştirin.

anahtar / değer çiftleri anlamına gelen nesneler (Python'da sözlük, Java'da hashmap, JavaScript'te nesne)

Özdeş bir belgeyi almak için XML hiçbir haritalama geri bir anahtar / değer çifti, bir nitelik ya da olup olmadığı bilinmemektedir, nedeni vardır <key>value</key>bilgileri kaybolur nedenle ki.

Bana sorarsanız, özellikler başlamak için bir kesmek; sonra tekrar HTML için iyi çalıştılar.


4

Muhtemelen en basit yol XML'i sözlüklere ayrıştırmak ve daha sonra bunu simplejson ile serileştirmektir.


4

Doğrudan bir dönüşüm için gitmemenizi öneririm. XML'i bir nesneye, sonra nesneden JSON'a dönüştürün.

Bence bu, XML ve JSON'un nasıl karşılık geldiğine dair daha net bir tanım sunuyor.

Doğru olması zaman alır ve bazılarını oluşturmanıza yardımcı olacak araçlar bile yazabilirsiniz, ancak kabaca şöyle görünecektir:

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

Basit XML snipleri için buldum, düzenli ifade kullanmak sorunları kurtaracaktı. Örneğin:

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

@Dan'ın dediği gibi XML ayrıştırma ile bunu yapmak için, hepsi farklı bir çözüm yoktur çünkü veriler farklıdır. Benim önerim lxml kullanmak. Json için bitmemiş olsa da, lxml.objectify sessiz iyi sonuçlar verir:

>>> 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
ancak yinelenen düğümleri kaldırır
Pooya

2

XML ayrıştırma için yerleşik libs oldukça iyi olsa da ben lxml kısmi duyuyorum .

Ancak RSS beslemelerini ayrıştırmak için Atom'u da ayrıştırabilen Universal Feed Parser'ı öneriyorum . Ana avantajı, çoğu yanlış şekillendirilmiş beslemeyi bile sindirebilmesidir.

Python 2.6 zaten bir JSON ayrıştırıcısı içerir, ancak simplejson olarak geliştirilmiş hıza sahip daha yeni bir sürüm mevcuttur .

Bu araçlar ile uygulamanızı oluşturmak o kadar zor olmamalı.


2

Cevabım , tüm xml'yi json'a dönüştürmeniz gerekmediği belirli (ve biraz yaygın) bir durumu ele alıyor , ancak ihtiyacınız olan xml'nin belirli bölümlerini çaprazlamak / erişmek ve hızlı olması gerekiyor ve basit (json / dict benzeri işlemler kullanarak).

Yaklaşmak

Bunun için, bir xml'yi kullanarak etree'yi ayrıştırmanın lxmlsüper hızlı olduğunu not etmek önemlidir . Diğer cevapların çoğundaki yavaş kısım ikinci geçiştir: etree yapısının (genellikle python-land'da) geçilmesi, json'a dönüştürülmesi.

Bu da beni bu durum için en iyi bulduğum yaklaşıma götürüyor: lxmlxml'yi kullanarak ayrıştırma ve etree düğümlerini (tembel olarak) sarmalama, bunlara dikte benzeri bir arayüz sağlama.

kod

İşte 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),
    )

Bu uygulama tam değildir, örneğin, bir öğenin hem metin hem de niteliklere veya hem metin hem de çocuklara sahip olduğu durumları temiz bir şekilde desteklemez (yalnızca yazdığımda ihtiyacım olmadığı için ...) Kolay olmalı yine de geliştirmek için.

hız

Yalnızca xml'in belirli öğelerini işlemem gerektiğinde özel kullanım durumumda, bu yaklaşım @Martin Blech'in xmltodict'ini kullanmaya ve ardından doğrudan dikdörtgeni geçmeye kıyasla 70 (!) Faktörle şaşırtıcı ve çarpıcı bir hız kazandı .

Bonus

Bir bonus olarak, yapımız zaten dikte olduğu xml2jsoniçin, ücretsiz olarak başka bir alternatif uygulama elde ediyoruz . Sadece diktine benzer yapımızı geçirmemiz gerekiyor json.dumps. Gibi bir şey:

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

attr_prefixXML'niz öznitelikler içeriyorsa , anahtarların geçerli json anahtarları olduğundan emin olmak için bazı alfasayısal (örn. "ATTR_") kullanmanız gerekir .

Bu kısmı karşılaştırmadım.


Bunu denersem 'ETreeDictWrapper' türünde bir nesne JSON serileştirilemezjson.dumps(tree) diyor
Vlad T.

2

Python XML ile bir şey yaptığımda neredeyse her zaman lxml paketini kullanın. Çoğu insanın lxml kullandığından şüpheleniyorum. Xmltodict kullanabilirsiniz, ancak XML'i tekrar ayrıştırmanın cezasını ödemek zorunda kalacaksınız.

XML'i json'a lxml ile dönüştürmek için:

  1. XML belgesini lxml ile ayrıştırma
  2. LXML'i bir dikteye dönüştür
  3. Listeyi json'a dönüştür

Projelerimde aşağıdaki sınıfı kullanıyorum. ToJson yöntemini kullanın.

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)

Dahili ana çıktı :

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

Hangi bu xml bir dönüşüm:

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




1

Declxml kullanabilirsiniz. Çoklu özellikler ve karmaşık iç içe destek gibi gelişmiş özelliklere sahiptir. Bunun için basit bir işlemci yazmanız yeterlidir. Ayrıca aynı kodla JSON'a da dönüştürebilirsiniz. Oldukça basit ve belgeler harika.

Bağlantı: https://declxml.readthedocs.io/en/latest/index.html


-1

Python'da veri hazırlama : Önce JSON oluşturmak için python'da veri hazırlamanız gerekir. Verileri hazırlamak için Python'daki Liste ve Sözlük'ü kullanabiliriz.

Python Listesi <==> JSON Dizisi

Python Sözlüğü <==> JSON Nesnesi (Anahtar Değer Biçimi) Daha fazla bilgi için bunu kontrol edin

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


Stack Overflow'a hoş geldiniz! Bağlantılar bilgiyi paylaşmanın harika bir yolu olsa da, gelecekte kırılırlarsa soruya gerçekten cevap vermezler. Cevabınızı soruyu cevaplayan bağlantının temel içeriğini ekleyin. İçeriğin buraya sığmayacak kadar karmaşık veya çok büyük olması durumunda, önerilen çözümün genel fikrini açıklayın. Her zaman orijinal çözümün web sitesine bir bağlantı referansı tutmayı unutmayın. Bkz: İyi bir cevabı nasıl yazarım?
sɐunıɔ ןɐ qɐp

-4

Verileri JSON biçiminde göstermek için

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

Gelen json biz anahtar ve değer biçiminde bir veri repesent

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

Verileri XML biçiminde temsil etmek

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.