我在网上看到过相当多笨拙的XML->JSON代码,并与Stack的用户进行了一些互动,我相信这群人能比谷歌结果的前几页提供更多的帮助。

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

这个公共weather.com RSS提要是我们将要解析的内容的一个很好的例子(我们实际的weather.com提要包含额外的信息,因为与他们有合作关系)。

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


当前回答

我的答案针对的是特定的(有点常见的)情况,在这种情况下,您实际上不需要将整个xml转换为json,但您需要遍历/访问xml的特定部分,并且您需要它是快速和简单的(使用json/dict-like操作)。

方法

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

这使我采用了我认为最适合这种情况的方法:使用lxml解析xml,然后(惰性地)包装树节点,为它们提供一个类似字典的接口。

Code

代码如下:

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然后直接遍历字典相比,这种方法提供了惊人的70倍加速(!)。

奖金

作为奖励,由于我们的结构已经是dict-like的,我们可以免费获得xml2json的另一种替代实现。我们只需要将dict-like结构传递给json.dumps。喜欢的东西:

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

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

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

其他回答

虽然用于XML解析的内置库非常好,但我更倾向于lxml。

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

Python 2.6已经包含了一个JSON解析器,但是速度有所提高的新版本是simplejson。

有了这些工具,构建你的应用应该不会那么困难。

你可能想看看http://designtheory.org/library/extrep/designdb-1.0.pdf。这个项目从一个大型XML文件库的XML到JSON转换开始。在转换过程中进行了大量研究,并生成了最简单直观的XML -> JSON映射(在本文前面有描述)。总之,将所有内容转换为JSON对象,并将重复块作为对象列表。

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

没有映射回XML以获得相同的文档,原因是,键/值对是属性还是<key>value</key>,因此该信息丢失。

如果你问我,我会说属性是一个入门;但它们在HTML上也很好用。

如果有些时候你只得到响应代码而不是所有的数据,那么像json解析这样的错误将会存在,所以你需要将它转换为文本

import xmltodict

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

有一种方法可以将基于xml的标记传输为JSON,允许它无损地转换回原始形式。见http://jsonml.org/。

它是一种JSON的XSLT。我希望这对你有帮助

我不久前在github上发表了一篇文章。

https://github.com/davlee1972/xml_to_json

这个转换器是用Python编写的,将一个或多个XML文件转换为JSON / JSONL文件

它需要一个XSD模式文件来找出嵌套的json结构(字典vs列表)和json等效数据类型。

python xml_to_json.py -x PurchaseOrder.xsd PurchaseOrder.xml

INFO - 2018-03-20 11:10:24 - Parsing XML Files..
INFO - 2018-03-20 11:10:24 - Processing 1 files
INFO - 2018-03-20 11:10:24 - Parsing files in the following order:
INFO - 2018-03-20 11:10:24 - ['PurchaseOrder.xml']
DEBUG - 2018-03-20 11:10:24 - Generating schema from PurchaseOrder.xsd
DEBUG - 2018-03-20 11:10:24 - Parsing PurchaseOrder.xml
DEBUG - 2018-03-20 11:10:24 - Writing to file PurchaseOrder.json
DEBUG - 2018-03-20 11:10:24 - Completed PurchaseOrder.xml

我也有一个后续的xml到拼花转换器,以类似的方式工作

https://github.com/blackrock/xml_to_parquet