使用Python将XML转换为JSON吗?

Converting XML to JSON using Python?

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

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

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

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


xmltodict(完全公开:我写了它)可以帮助您按照此"标准"将XML转换为dict + list + string结构。它是基于Expat的,因此速度非常快,不需要将整个XML树加载到内存中。

一旦有了该数据结构,就可以将其序列化为JSON:

1
2
3
4
import xmltodict, json

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


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

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

因此基础设施就在那里。


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

例如,以下XML:

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

通过BadgerFish约定转换为:

1
2
3
4
5
6
{
  'p': {
    '@id': 1,
    '$': 'text'
  }
}

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

1
2
3
4
5
{
  'p': {
    '$t': 'text'
  }
}

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

1
2
3
{
  'p': 'text'
}

可以使用相同的方法将XML转换为JSON,也可以将JSON转换为XML
约定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> 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>
]

披露:我写了这个库。希望它对将来的搜索者有所帮助。


有一种方法可以将基于XML的标记传输为JSON,从而可以无损地将其转换回其原始形式。请参阅http://jsonml.org/。

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


这是我为此创建的代码。没有内容的解析,只是普通的转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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()

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

1
2
3
4
5
6
import xmltodict

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

您可能需要看一下http://designtheory.org/library/extrep/designdb-1.0.pdf。该项目首先从大型XML文件库的XML到JSON转换开始。转换过程中进行了大量研究,并且生成了最简单的直观XML-> JSON映射(在本文的开头进行了介绍)。总之,将所有内容都转换为JSON对象,并将重复的块作为对象列表。

表示键/值对的对象(Python中的字典,Java中的哈希图,JavaScript中的对象)

没有映射回XML来获取相同文档的原因,原因是,未知键/值对是属性还是value,因此会丢失信息。

如果您问我,属性是一个开始。然后它们再次适用于HTML。


好吧,最简单的方法可能只是将XML解析为字典,然后使用simplejson对其进行序列化。


我建议不要直接转换。将XML转换为对象,然后从该对象转换为JSON。

我认为,这为XML和JSON的对应关系提供了更清晰的定义。

正确需要花费一些时间,您甚至可以编写工具来帮助您生成其中的一些内容,但是看起来大致是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 ):
    ...


对于可能仍需要此功能的任何人。这是执行此转换的更新的简单代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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


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

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

  • 使用lxml解析XML文档
  • 将lxml转换为dict
  • 将列表转换为json
  • 我在项目中使用以下课程。使用toJson方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    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 = '', 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"".

            :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的输出为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
       "system": {
           "attributes": {},
           "product": {
               "attributes": {},
               "demod": {
                   "attributes": {},
                   "frequency": {
                       "attributes": {
                           "units":"MHz",
                           "value":"2.215"
                        },
                       "blah": {
                           "attributes": {
                               "value":"1"
                            }
                        }
                    }
                }
            }
        }
    }

    这是此xml的转换:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <system>
        <product>
            <demod>
                <frequency value='2.215' units='MHz'>
                    <blah value='1'/>
                </frequency>
            </demod>
        </product>
    </system>

    我发现对于简单的XML片段,使用正则表达式可以省去麻烦。例如:

    1
    2
    3
    4
    # <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给出了良好的效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    >>> from lxml import objectify
    >>> root = objectify.fromstring("""
    ... <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    ...   1
    ...   1.2
    ...   1
    ...   true
    ...   <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'


    虽然用于XML解析的内置库相当不错,但我偏爱lxml。

    但是对于解析RSS feed,我建议使用Universal Feed Parser,它也可以解析Atom。
    它的主要优点是它甚至可以消化大多数畸形的饲料。

    Python 2.6已经包含一个JSON解析器,但具有更高速度的较新版本可以作为simplejson使用。

    使用这些工具构建您的应用程序应该没有那么困难。


    这里的东西一直在积极维护,到目前为止,我最喜欢的是:python中的xml2json


    我的答案针对的是特定情况(在某种程度上是常见的情况),您实际上不需要将整个xml转换为json,但是您需要遍历/访问xml的特定部分,并且需要快速并且简单(使用类似于json / dict的操作)。

    途径

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

    这使我想到了最适合这种情况的方法:使用lxml解析xml,然后(缓慢地)包装etree节点,为它们提供类似dict的接口。

    这是代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    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。就像是:

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

    如果您的xml包含属性,则需要使用一些字母数字attr_prefix(例如" ATTR_"),以确保键是有效的json键。

    我还没有对这部分进行基准测试。


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

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


    签出lxml2json(公开:我写了)

    https://github.com/rparelius/lxml2json

    它非常快速,轻巧(仅需要lxml),优点之一是您可以控制是否将某些元素转换为列表或字典


    jsonpickle,或者如果您使用的是feedparser,则可以尝试feed_pa??rser_to_json.py


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

    Python列表<==> JSON数组

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

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


    以JSON格式表示数据

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

    在json中,我们以键和值格式表示数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
       "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格式表示数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <!-- In xml we write a code under a key you can take any key -->
    <info> <!-- key open -->

    <name> john </name>
     20 </age>
    <gender> male </gender>

     
    <item> New kP college </item>
    <item> Greater Kailash </item>
    <item> New Delhi </item>
    </address>

    <jobs>
     <item>
      Developer
      <place>Noida</place>
     </item>

     <item>
      Designer
      <place>Gurugram</place>
     </item>
     
     <item>
      Developer
      <place>Faridabad</place>
     </item>
    </jobs>

    </info> <!-- key close-->