使用Python将XML转换为JSON吗?


170

我在网络上看到了相当多的不费钱的XML-> JSON代码,并且与Stack的用户进行了一些互动,我坚信这一人群比Google搜索结果的前几页可以提供更多帮助。

因此,我们正在解析一个天气供稿,我们需要在许多网站上填充天气小部件。我们现在正在研究基于Python的解决方案。

这个公共weather.com RSS feed是我们要解析的一个很好的例子(由于有w / them的合作关系,我们实际的weather.com feed包含其他信息)。

简而言之,我们应该如何使用Python将XML转换为JSON?

Answers:


61

XML和JSON之间没有“一对一”的映射,因此将一个转换为另一个必须了解您要如何处理结果。

话虽这么说,Python的标准库有几个用于解析XML的模块(包括DOM,SAX和ElementTree)。从Python 2.6开始,该json模块中包含了将Python数据结构与JSON相互转换的支持。

因此基础架构就在那里。


2
xmljson IMHO是开箱即用,支持各种约定的最快方法。pypi.org/project/xmljson
nitinr708 '18

在新的答案中已经提到了它。它仍然仅覆盖有效XML构造的一小部分,但可能是人们实际使用的大多数。
丹·伦斯基

281

xmltodict(完全公开:我写了它)可以帮助您按照此“标准”将XML转换为dict + list + string结构。它是基于Expat的,因此它非常快并且不需要将整个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如果我从Django模型文件中创建一个json文件。如何映射我的xml文件,以便将必填字段的xml转换为json?
2014年

1
@sayth我认为您应该将此作为单独的SO问题发布。
马丁·布莱奇

@马丁·布莱奇 我添加了一个问题,但是很难适应,我是一个初学者,因此已提供了尽可能多的信息,但是我希望您可能需要更多的信息stackoverflow.com/q/23676973/461887
比如

这么长时间后,我感到有些惊讶,xmltodict在某些Linux发行版中不是“标准”库。尽管从我们可以阅读的内容来看似乎可以直接完成工作,但是不幸的是,我将使用另一种解决方案,例如xslt转换
sancelot

非常感谢您编写了这个很棒的库。尽管bs4可以完成xml命令的工作,但是使用该库非常容易
Tessaracter19年

24

您可以使用xmljson库使用不同的XML JSON约定进行转换。

例如,以下XML:

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

通过BadgerFish约定转换为:

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

并通过GData约定添加到其中(不支持属性):

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

...,并通过Parker约定加入(不支持属性):

{
  '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
那是一个非常酷的库,但是请阅读如何提供个人开源库?然后再发布更多答案以炫耀它。
马丁·彼得斯

1
感谢@MartijnPieters-我刚刚经历了这一点,将确保我坚持这一点。
S阿南德

1
感谢Anand提供的解决方案–它看起来运作良好,没有外部依赖性,并且为使用不同约定处理属性提供了很大的灵活性。正是我需要的,这是我找到的最灵活,最简单的解决方案。
mbbeme '16

感谢Anand-不幸的是,我无法使用utf8编码来解析XML。通过源代码,似乎通过XMLParser(..)的编码集被忽略了
Patrik Beck

@PatrikBeck能否请您分享一个带有utf8编码的XML中断示例?
S阿南德

11

如果一段时间您仅获得响应代码而不是所有数据,则将出现诸如json parse之类的错误,因此您需要将其转换为文本

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/

这是JSON的XSLT。我希望你觉得这对你有帮助


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':“测试服务”,它来自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-instance “> <modelVersion> 4.0.0 </ modelVersion>
hrbdg

5

您可能想看看http://designtheory.org/library/extrep/designdb-1.0.pdf。该项目首先从大型XML文件库的XML到JSON转换开始。转换过程中进行了大量研究,并且生成了最简单的直观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

正如@Dan所说,要通过XML解析来做到这一点,因为数据是不同的,所以没有一对一的解决方案。我的建议是使用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转换为json,但是您需要遍历/访问xml的特定部分,并且需要快速进行处理,并且简单(使用类似于json / dict的操作)。

方法

为此,需要注意的是,将xml解析为etree lxml超级快。其他大多数答案中最慢的部分是第二遍:遍历etree结构(通常在python-land中),将其转换为json。

这使我lxml想到了最适合这种情况的方法:使用解析xml ,然后(延迟地)包装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的特定元素,与使用@Martin Blech的xmltodict然后直接遍历该dict 相比,此方法可提供惊人的惊人的加速70(!)

奖金

另外,由于我们的结构已经像字典一样,我们可以xml2json免费获得另一种替代实现。我们只需要将类似于dict的结构传递给即可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序列化
Vlad

2

当我在python中对XML做任何事情时,我几乎总是使用lxml包。我怀疑大多数人都使用lxml。您可以使用xmltodict,但是您将不得不付出再次解析XML的代价。

要使用lxml将XML转换为json,请执行以下操作:

  1. 使用lxml解析XML文档
  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)

内置main的输出为:

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

您可以使用declxml。它具有高级功能,例如多属性和复杂的嵌套支持。您只需要为此编写一个简单的处理器即可。同样,使用相同的代码,您也可以转换回JSON。这非常简单,文档也很棒。

链接:https//declxml.readthedocs.io/en/latest/index.html


-1

使用Python准备数据:首先要创建JSON,您需要使用python准备数据。我们可以在Python中使用列表和字典来准备数据。

Python列表<==> JSON数组

Python字典<==> JSON对象(键值格式)请检查此以获取更多详细信息

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


欢迎使用Stack Overflow!虽然链接是共享知识的好方法,但是如果将来它们断开,它们将无法真正回答问题。在回答中添加回答问题的链接的基本内容。如果内容太复杂或太大而无法容纳在此处,请描述所提出解决方案的总体思路。请记住,始终保留指向原始解决方案网站的链接参考。请参阅:我如何写一个好的答案?
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-->

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.