使用Python将XML转换为JSON?

时间:2008-10-10 14:19:44

标签: python json xml converter

我在网上看到了相当多的笨拙的XML-> JSON代码,并且与Stack的用户进行了一段时间的互动,我相信这群人可以提供超过Google搜索结果前几页的帮助。可以。

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

此公开weather.com RSS feed是我们要解析的一个很好的例子(我们的实际weather.com Feed包含其他信息,因为它们与他们合作)。

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

20 个答案:

答案 0 :(得分:252)

xmltodict(完全披露:我写过)可以帮助您将XML转换为dict + list + string结构,遵循此"standard"。它基于Expat,所以它非常快,不需要在内存中加载整个XML树。

拥有该数据结构后,您可以将其序列化为JSON:

import xmltodict, json

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

答案 1 :(得分:51)

XML和JSON之间没有“一对一”映射,因此将一个转换为另一个必然需要了解您希望的结果。

话虽如此,Python的标准库有several modules for parsing XML(包括DOM,SAX和ElementTree)。从Python 2.6开始,json module中包含对与JSON进行Python数据结构转换的支持。

所以基础设施就在那里。

答案 2 :(得分: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()

答案 3 :(得分:6)

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

这是一种JSON的XSLT。我希望你觉得它很有用

答案 4 :(得分:5)

您可能需要查看http://designtheory.org/library/extrep/designdb-1.0.pdf。该项目从一个大型XML文件库的XML到JSON转换开始。在转换中进行了大量研究,并且最简单直观的XML - &gt;生成了JSON映射(在文档的早期描述)。总之,将所有内容转换为JSON对象,并将重复块作为对象列表。

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

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

如果你问我,属性是一个黑客入手;然后他们再次为HTML做好了。

答案 5 :(得分:4)

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

答案 6 :(得分:3)

我建议不要直接转换。将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 ):
    ...

答案 7 :(得分:2)

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

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

Python 2.6已包含JSON解析器,但较新的version with improved speed可用simplejson

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

答案 8 :(得分:2)

我找到了简单的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给出了安静的好结果:

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

答案 9 :(得分:2)

当我在python中使用XML时,我几乎总是使用lxml包。我怀疑大多数人都使用lxml。你可以使用xmltodict,但你必须支付再次解析XML的代价。

使用lxml将XML转换为json:

  1. 使用lxml
  2. 解析XML文档
  3. 将lxml转换为dict
  4. 将列表转换为json
  5. 我在我的项目中使用以下类。使用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)
    

    内置主要的输出为:

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

答案 10 :(得分:1)

我的回答解决了并非真正需要将整个xml 转换为json的具体(并且有点常见)的情况,但您需要的是遍历/访问特定部分xml,你需要快速简单(使用类似json / dict的操作)。

方法

为此,重要的是要注意使用lxml将xml解析为etree是非常快的。大多数其他答案的缓慢部分是第二遍:遍历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&相比,这种方法给出了令人惊讶且惊人的加速因子70(!) #39; s xmltodict然后直接遍历dict。

加成

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

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

如果您的xml包含属性,则需要使用一些字母数字attr_prefix(例如&#34; ATTR _&#34;),以确保密钥是有效的json密钥。

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

答案 11 :(得分:1)

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

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

答案 12 :(得分:1)

签出lxml2json(公开:我写了它)

https://github.com/rparelius/lxml2json

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

答案 13 :(得分:1)

对任何可能仍然需要这个的人。这是进行此转换的更简单的新代码。

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

答案 14 :(得分:1)

这里的内容得到了积极维护,到目前为止我最喜欢的是:xml2json in python

答案 15 :(得分:1)

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

答案 16 :(得分:0)

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

Python列表<==> JSON数组

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

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

答案 17 :(得分: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>

输出

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

答案 18 :(得分:0)

我不久前在 github 上发布了一个..

https://github.com/davlee1972/xml_to_json

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

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

df1['var4'] = np.where(df['Year'] == 2010, df2[2010],
              np.where(df['Year'] == 2011, df2[2011],
              np.where(df['Year'] == 2012, df2[2012], 'NaN')))

我还有一个后续的 xml to parquet 转换器,它以类似的方式工作

https://github.com/blackrock/xml_to_parquet

答案 19 :(得分:-1)

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