使用Python将XML转换为JSON?

213 人关注

I've seen a fair share of ungainly XML->JSON code on the web, and having interacted with Stack's users for a bit, I'm convinced that this crowd can help more than the first few pages of Google results can.

所以,我们正在解析一个天气信息,我们需要在众多的网站上填充天气小部件。 我们现在正在研究基于Python的解决方案。

This public weather.com的RSS订阅 是我们要解析的一个很好的例子( 由于与他们的合作关系,我们实际的weather.com feed包含了额外的信息。 ).

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

python
json
xml
converter
Pete Karl II
Pete Karl II
发布于 2008-10-10
20 个回答
Martin Blech
Martin Blech
发布于 2021-02-24
已采纳
0 人赞同

xmltodict (完全公开:我写的)可以帮助你将你的XML转换为dict+list+string结构,具体方法如下 "标准" . It is 外籍人士 -基于,所以它非常快,不需要在内存中加载整个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的必填字段?
@sayth 我认为你应该把这个问题作为一个单独的SO问题来发布。
@Martin Blech. 我加了一个问题,但它相当难装入SO,我是一个初学者,所以已经尽可能多地提供了信息,但我估计你可能需要更多的说明。 stackoverflow.com/q/23676973/461887
经过这么长时间,我有点惊讶xmltodict在一些Linux发行版中不是一个 "标准 "库。尽管从我们所读到的内容来看,它似乎在直接做工作,但不幸的是,我将使用另一种解决方案,如xslt转换。
非常感谢你编写这个神奇的库。虽然 bs4 可以完成xml到dict的工作,但是使用这个库是非常容易的。
Dan Lenski
Dan Lenski
发布于 2021-02-24
0 人赞同

在XML和JSON之间没有 "一对一 "的映射,所以将一个转换为另一个必然需要对你想要的东西有一定的了解。 do with the results.

这就是说,Python的标准库有 用于解析XML的几个模块 (包括DOM、SAX和ElementTree)。 从Python 2.6开始,支持将Python数据结构转换为JSON,也支持从JSON转换为Python。 json module .

因此,基础设施是存在的。

xmljson IMHO是使用最快速的,它支持开箱即用的各种约定。 pypi.org/project/xmljson
它已经在较新的答案中提到了。它仍然只包括有效的XML结构的一小部分,但可能是人们在实践中使用的大部分。
S Anand
S Anand
发布于 2021-02-24
0 人赞同

你可以使用 xmljson 转换器,使用不同的 XML JSON conventions .

例如,这个XML。

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

通过以下方式进行翻译BadgerFish公约 into this:

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

并通过GData公约到这里面(不支持属性)。

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

... 并通过帕克公约到这里面(不支持属性)。

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

披露:我写了这个库。希望它能帮助未来的搜索者。

那是一个相当酷的图书馆,但请务必阅读 如何提供个人开放源码库? 在你发表更多的答案来炫耀它之前。
谢谢 @MartijnPieters -- 我刚刚经历了这个,并将确保我坚持这样做。
谢谢Anand的解决方案--它似乎工作得很好,没有外部依赖性,并且在如何使用不同的约定处理属性方面提供了很大的灵活性。这正是我所需要的,也是我找到的最灵活和最简单的解决方案。
谢谢Anand - 不幸的是,我不能让它解析具有utf8编码的XML。翻阅资料,似乎通过XMLParser(.)设置的编码被忽略了。
@PatrikBeck 你能不能分享一个用utf8编码的XML断裂的小例子?
Akshay Kumbhar
Akshay Kumbhar
发布于 2021-02-24
0 人赞同

If some time you get only response code 而不是所有的数据,那么 像json解析的错误 将在那里,所以你需要将其转换为 text

import xmltodict
data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 
    
jnhustin
jnhustin
发布于 2021-02-24
0 人赞同

对于任何可能仍然需要这个的人。这里有一个较新的、简单的代码来做这个转换。

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 3.7中起作用,尽管不幸的是,如果你的xml中有某些值,它会在键名中添加一些意想不到的数据,例如,根级节点上的xmlns标签在每个节点键中都显示为这样。 {'{ maven.apache.org/POM/4.0.0 }artifactId': 'test-service', which came from xml like this: <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>
gman
类型错误:'ElementTree'对象不是可迭代的
从list(xml)改为xml.iter(),以避免类型错误:'ElementTree'对象不是可重复的。
Paulo Vj
Paulo Vj
发布于 2021-02-24
0 人赞同

这是我为此编写的代码。没有对内容进行解析,只是简单的转换。

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:
                    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()
    
themihai
themihai
发布于 2021-02-24
0 人赞同

有一种方法可以将基于XML的标记作为JSON来传输,这样可以将其无损地转换为原始形式。见 http://jsonml.org/ .

这是一种JSON的XSLT。我希望你能发现它的帮助

pykler
pykler
发布于 2021-02-24
0 人赞同

你可能想看一下 http://designtheory.org/library/extrep/designdb-1.0.pdf . This project starts off with an XML to JSON conversion of a large library of XML files. There was much research done in the conversion, and the most simple intuitive XML -> JSON mapping was produced (it is described early in the document). In summary, convert everything to a JSON object, and put repeating blocks as a list of objects.

意味着键/值对的对象(Python中的字典,Java中的hashmap,JavaScript中的对象)。

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

如果你问我,属性是一个开始的黑客;再者,它们对HTML很有效。

Michael Anderson
Michael Anderson
发布于 2021-02-24
0 人赞同

我建议不要进行直接转换。把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 ):
    
如果没有提示变量 xml_node 的来源(它是什么类型?它的 .xpath 方法是做什么的?),这个答案就没有什么用。
dguaraglia
dguaraglia
发布于 2021-02-24
0 人赞同

那么,最简单的方法可能就是把XML解析成字典,然后用simplejson进行序列化。

shrewmouse
shrewmouse
发布于 2021-02-24
0 人赞同

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

To convert XML to json with lxml you:

  • Parse XML document with lxml
  • Convert lxml to a dict
  • Convert list to 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)
    

    The output from the built in 主要 is:

    "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>
        
    这是否处理标签内的文本(如: <text>Some text</text> )?
    已经有一段时间了。 没有,但很容易做到。添加 'text: self.element.text toDict
    Andrew_1510
    Andrew_1510
    发布于 2021-02-24
    0 人赞同

    我发现对于简单的XML剪裁,使用正则表达式会省去很多麻烦。 比如说。

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

    要通过XML解析来做,正如@Dan所说,没有一个万能的解决方案,因为数据是不同的。我的建议是使用lxml。虽然没有完成到json。lxml.objectify give quiet good results:

    >>> 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'
        
    但它会删除重复的节点
    Luka Marinko
    Luka Marinko
    发布于 2021-02-24
    0 人赞同

    虽然用于XML解析的内置库相当好,但我偏爱的是 lxml .

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

    Python 2.6已经包含了一个JSON分析器,但是一个较新的 version with improved speed 可作为 simplejson .

    有了这些工具,建立你的应用程序不应该是那么困难。

    0 人赞同

    我的回答是针对具体的(有点常见的)情况,即你 不太需要转换整个xml。 到json,但你需要的是遍历/访问xml的特定部分,而且你需要它是 快速 ,以及 simple (使用类似json/dict的操作)。

    Approach

    For this, it is important to note that parsing an xml to etree using lxml is super 快速. The slow part in most of the other answers is the second pass: traversing the etree structure (usually in python-land), converting it to json.

    Which leads me to the approach I found best for this case: parsing the xml using lxml ,以及then wrapping the etree nodes (lazily), providing them with a dict-like interface.

    Here's the 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)(
                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(
            attr_prefix = '@',
            list_tags = set(list_tags),
    

    这个实现并不完整,例如,它没有干净地支持一个元素既有文本又有属性,或者既有文本又有子元素的情况(只是因为我写的时候不需要......),不过,改进它应该很容易。

    Speed

    在我的特定用例中,我只需要处理xml的特定元素,这种方法给出了一个令人惊讶和惊人的速度提高了70倍(!)。相比之下,使用@Martin Blech的xmltodict然后直接遍历dict。

    Bonus

    作为奖励,由于我们的结构已经类似于dict,我们可以免费获得另一种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序列化
    Robert Parelius
    Robert Parelius
    发布于 2021-02-24
    0 人赞同

    check out lxml2json (disclosure: I wrote it)

    https://github.com/rparelius/lxml2json

    它非常快速、轻量级(只需要lxml),一个优点是你可以控制某些元素是否被转换为列表或数据集。

    choonkeat
    choonkeat
    发布于 2021-02-24
    0 人赞同

    jsonpickle 或者如果你使用的是feedparser,你可以试试 feed_parser_to_json.py

    srth12
    srth12
    发布于 2021-02-24
    0 人赞同

    你可以使用declxml。它有先进的功能,如多属性和复杂的嵌套支持。你只需要为它写一个简单的处理器。同样的代码,你也可以转换回JSON。它是相当直接的,而且文档也很好。

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

    Liju
    Liju
    发布于 2021-02-24
    0 人赞同

    如果你不想使用任何外部库和第三方工具,请尝试以下代码。

    import re
    import json
    def getdict(content):
        res=re.findall("<(?P<var>\S*)(?P<attr>[^/>]*)(?:(?:>(?P<val>.*?)</(?P=var)>)|(?:/>))",content)
        if len(res)>=1:
            attreg="(?P<avr>\S+?)(?:(?:=(?P<quote>['\"])(?P<avl>.*?)(?P=quote))|(?:=(?P<avl1>.*?)(?:\s|$))|(?P<avl2>[\s]+)|$)"
            if len(res)>1:
                return [{i[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,i[1].strip())]},{"$values":getdict(i[2])}]} for i in res]
            else:
                return {res[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,res[1].strip())]},{"$values":getdict(res[2])}]}
        else:
            return content
    with open("test.xml","r") as f:
        print(json.dumps(getdict(f.read().replace('\n',''))))
    
    <details class="4b" count=1 boy>
        <name type="firstname">John</name>
        <age>13</age>
        <hobby>Coin collection</hobby>
        <hobby>Stamp collection</hobby>
        <address>
            <country>USA</country>
            <state>CA</state>
        </address>
    </details>
    <details empty="True"/>
    <details/>
    <details class="4a" count=2 girl>
        <name type="firstname">Samantha</name>
        <age>13</age>
        <hobby>Fishing</hobby>
        <hobby>Chess</hobby>
        <address current="no">
            <country>Australia</country>
            <state>NSW</state>
        </address>
    </details>
    

    Output

    "details": [ "@attributes": [ "class": "4b" "count": "1" "boy": "" "$values": [ "name": [ "@attributes": [ "type": "firstname" "$values": "John" "age": [ "@attributes": [] "$values": "13" "hobby": [ "@attributes": [] "$values": "Coin collection" "hobby": [ "@attributes": [] "$values": "Stamp collection" "address": [ "@attributes": [] "$values": [ "country": [ "@attributes": [] "$values": "USA" "state": [ "@attributes": [] "$values": "CA" "details": [ "@attributes": [ "empty": "True" "$values": "" "details": [ "@attributes": [] "$values": "" "details": [ "@attributes": [ "class": "4a" "count": "2" "girl": "" "$values": [ "name": [ "@attributes": [ "type": "firstname" "$values": "Samantha" "age": [ "@attributes": [] "$values": "13" "hobby": [ "@attributes": [] "$values": "Fishing" "hobby": [ "@attributes": [] "$values": "Chess" "address": [ "@attributes": [ "current": "no" "$values": [ "country": [ "@attributes": [] "$values": "Australia" "state": [ "@attributes": [] "$values": "NSW"
    truthadjustr
    truthadjustr
    发布于 2021-02-24
    0 人赞同

    这里的东西是积极维护的,到目前为止是我最喜欢的。 xml2json in python

    这已被废弃。
    确实如此。我们能知道一个github repo转移到只读/存档的确切日期吗?我只看到最后一次提交是2019年。谢谢。
    "该模块已被废弃,将不再更新(2019年5月)"
    David Lee
    David Lee
    发布于 2021-02-24
    0 人赞同

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

    https://github.com/davlee1972/xml_to_json

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

    它需要一个XSD模式文件来弄清嵌套的json结构(字典与列表)和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