首页 文章

使用Python将XML转换为JSON?

提问于
浏览
135

我在网上看到了相当多的笨拙的XML-> JSON代码,并且与Stack的用户进行了一些交互,我相信这群人可以比谷歌搜索结果的前几页更有帮助 .

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

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

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

20 回答

  • 15

    XML和JSON之间没有"one-to-one"映射,因此将一个转换为另一个必然需要了解您要对结果执行的操作 .

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

    所以基础设施就在那里 .

  • 1

    xmltodict(完全披露:我写了它)可以帮助您将XML转换为dict列表字符串结构,遵循此"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"]}}'
    
  • 0

    您可以使用xmljson库使用不同的XML JSON conventions进行转换 .

    例如,这个XML:

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

    通过BadgerFish convention翻译成:

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

    并通过GData convention进入此属性(不支持属性):

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

    ...并通过Parker convention进入此属性(不支持属性):

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

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

  • 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:
                    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()
    
  • 243

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

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

  • 5

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

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

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

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

  • 2

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

  • 7

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

    如果有一段时间你只得到 response code 而不是所有数据,那么 error like json parse 将在那里所以你需要将其转换为 text

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

    虽然用于XML解析的内置库非常好,但我对lxml不满意 .

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

    Python 2.6已经包含一个JSON解析器,但是更新的version with improved speed可用作simplejson .

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

  • 1

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

    要使用lxml将XML转换为json,您:

    • 使用lxml解析XML文档

    • 将lxml转换为dict

    • 将列表转换为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>
    
  • 3

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

  • 4

    我找到了简单的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'
    
  • 2

    我的答案解决了 don't really need to convert the entire xml 到json的特定(并且有些常见)情况,但你需要的是遍历/访问xml的特定部分,你需要它是 fastsimple (使用类似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的xmltodict然后直接遍历dict相比,这种方法给人一种惊喜和 striking speedup by a factor of 70 (!) .

    奖金

    作为奖励,由于我们的结构已经像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密钥 .

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

  • 1

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

  • 1

    看看lxml2json(披露:我写的)

    https://github.com/rparelius/lxml2json

    它非常快,重量轻(仅需要lxml),一个优点是你可以控制某些元素是否转换为列表或字符串

  • 6

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

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

  • 3

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

    Python中准备数据:首先要创建JSON,你需要在python中准备数据 . 我们可以使用Python中的List和Dictionary来准备数据 .

    Python List <==> JSON Array

    Python Dictionary <==> JSON Object(键值格式)选中此处以获取更多详细信息

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

相关问题