Python字典与XML互转

5 投票
5 回答
25879 浏览
提问于 2025-04-16 01:35

我需要用Python 2.4.4把XML转换成Python字典,反过来也可以。其实我只需要节点的名字和它们的值,属性我不在乎,因为我处理的XML里没有属性。我不能用ElementTree,因为它在2.4.4版本里不可用,而且由于工作环境的限制,我也不能使用第三方库。请问我该怎么做比较简单?有没有什么好的代码片段可以参考?

另外,如果没有简单的方法,那Python 2.4.4支持哪些其他的序列化格式呢?

5 个回答

2

在Python中,字典是没有顺序的,记住这一点。我有一段非常简单的代码,它很小,不需要任何外部模块。坏处是它不支持任何类型的XML属性,但你说过

我不担心属性

,所以这里是代码:

def d2x(d, root="root"):

    op = lambda tag: '<' + tag + '>'
    cl = lambda tag: '</' + tag + '>\n'
    ml = lambda v,xml: xml + op(key) + str(v) + cl(key)

    xml = op(root) + '\n' if root else ""

    for key,vl in d.iteritems():
        vtype = type(vl)
        if vtype is list: 
            for v in vl:
                xml = ml(v,xml)         
        if vtype is dict: xml = ml('\n' + d2x(vl,None),xml)         
        if vtype is not list and vtype is not dict: xml = ml(vl,xml)

    xml += cl(root) if root else ""

    return xml

使用示例:

mydict = {
"boolean":False,
"integer":12,
"float":3.1,
"listitems":["item1","item2"],
"string":"Hello world", 
"dictionary":{
    "key1":1,
    "key2":2,
    "dictindict":{
                "a":"aaa",
                "b":"bbb"
                }
            }
}
print d2x (mydict,"superxml")

这段代码会输出:

<superxml>
<string>Hello world</string>
<dictionary>
<key2>2</key2>
<key1>1</key1>
<dictindict>
<a>aaa</a>
<b>bbb</b>
</dictindict>
</dictionary>
<float>3.1</float>
<listitems>item1</listitems>
<listitems>item2</listitems>
<boolean>False</boolean>
<integer>12</integer>
</superxml>
5

我最近写了一些代码,用来把XML格式的数据转成Python可以用的数据结构,虽然我需要处理一些属性。我选择使用 xml.dom.minidom 而不是 ElementTree,原因差不多。我还没有在Python 2.4.4上测试过这个代码,但我觉得应该没问题。不过,我没有写一个可以反向生成XML的工具,不过你可以用我提供的 'lispy_string' 函数来实现这个功能。

我还加入了一些特定于我写的应用程序的快捷方式(在文档字符串中有解释),听起来你可能也会觉得这些快捷方式有用。简单来说,XML树在技术上可以理解为一个字典,里面包含了列表、字典、列表、字典,依此类推。我省略了中间的列表,除非真的需要,这样你就可以通过 dictname[element1][element2] 来引用元素,而不是 dictname[element1][0][element2][0] 这样复杂的方式。

处理属性的部分有点麻烦,我强烈建议你在对属性做任何操作之前先看看代码。

import sys
from xml.dom import minidom

def dappend(dictionary, key, item):
    """Append item to dictionary at key.  Only create a list if there is more than one item for the given key.
    dictionary[key]=item if key doesn't exist.
    dictionary[key].append(item) if key exists."""
    if key in dictionary.keys():
        if not isinstance(dictionary[key], list):
            lst=[]
            lst.append(dictionary[key])
            lst.append(item)
            dictionary[key]=lst
        else:
            dictionary[key].append(item)
    else:
        dictionary.setdefault(key, item)

def node_attributes(node):
    """Return an attribute dictionary """
    if node.hasAttributes():
        return dict([(str(attr), str(node.attributes[attr].value)) for attr in node.attributes.keys()])
    else:
        return None

def attr_str(node):
    return "%s-attrs" % str(node.nodeName)

def hasAttributes(node):
    if node.nodeType == node.ELEMENT_NODE:
        if node.hasAttributes():
            return True
    return False

def with_attributes(node, values):
    if hasAttributes(node):
        if isinstance(values, dict):
            dappend(values, '#attributes', node_attributes(node))
            return { str(node.nodeName): values }
        elif isinstance(values, str):
            return { str(node.nodeName): values,
                     attr_str(node): node_attributes(node)}
    else:
        return { str(node.nodeName): values }

def xmldom2dict(node):
    """Given an xml dom node tree,
    return a python dictionary corresponding to the tree structure of the XML.
    This parser does not make lists unless they are needed.  For example:

    '<list><item>1</item><item>2</item></list>' becomes:
    { 'list' : { 'item' : ['1', '2'] } }
    BUT
    '<list><item>1</item></list>' would be:
    { 'list' : { 'item' : '1' } }

    This is a shortcut for a particular problem and probably not a good long-term design.
    """
    if not node.hasChildNodes():
        if node.nodeType == node.TEXT_NODE:
            if node.data.strip() != '':
                return str(node.data.strip())
            else:
                return None
        else:
            return with_attributes(node, None)
    else:
        #recursively create the list of child nodes
        childlist=[xmldom2dict(child) for child in node.childNodes if (xmldom2dict(child) != None and child.nodeType != child.COMMENT_NODE)]
        if len(childlist)==1:
            return with_attributes(node, childlist[0])
        else:
            #if False not in [isinstance(child, dict) for child in childlist]:
            new_dict={}
            for child in childlist:
                if isinstance(child, dict):
                    for k in child:
                        dappend(new_dict, k, child[k])
                elif isinstance(child, str):
                    dappend(new_dict, '#text', child)
                else:
                    print "ERROR"
            return with_attributes(node, new_dict)

def load(fname):
    return xmldom2dict(minidom.parse(fname))

def lispy_string(node, lst=None, level=0):
    if lst==None:
        lst=[]
    if not isinstance(node, dict) and not isinstance(node, list):
        lst.append(' "%s"' % node)
    elif isinstance(node, dict):
        for key in node.keys():
            lst.append("\n%s(%s" % (spaces(level), key))
            lispy_print(node[key], lst, level+2)
            lst.append(")")
    elif isinstance(node, list):
        lst.append(" [")
        for item in node:
            lispy_print(item, lst, level)
        lst.append("]")
    return lst

if __name__=='__main__':
    data = minidom.parse(sys.argv[1])

    d=xmldom2dict(data)

    print d
3

这个问题 如何将Python字典转换为XML 列出了一些将数据转换为XML格式的方法。至于其他的转换格式,我觉得 pickle 模块是一个很不错的工具。

撰写回答