我如何映射到字典而不是列表?

时间:2010-11-06 10:49:35

标签: python xml list dictionary lxml

我有以下功能,它将lxml对象映射到字典的基本工作......

from lxml import etree 

tree = etree.parse('file.xml')
root = tree.getroot()

def xml_to_dict(el):
    d={}
    if el.text:
        print '***write tag as string'
        d[el.tag] = el.text
    else:
        d[el.tag] = {}
    children = el.getchildren()
    if children:
        d[el.tag] = map(xml_to_dict, children)
    return d

    v = xml_to_dict(root)

此刻它给了我......

>>>print v
{'root': [{'a': '1'}, {'a': [{'b': '2'}, {'b': '2'}]}, {'aa': '1a'}]}

但我想......

>>>print v
{'root': {'a': ['1', {'b': [2, 2]}], 'aa': '1a'}}

如何重写函数xml_to_dict(el)以便获得所需的输出?

这是我正在解析的xml,为了清楚起见。

<root>
    <a>1</a>
    <a>
        <b>2</b>
        <b>2</b>
    </a>
    <aa>1a</aa>
</root>

谢谢:)

2 个答案:

答案 0 :(得分:5)

好吧,map()将始终返回一个列表,因此简单的答案是“不要使用map()”。相反,通过循环children并将xml_to_dict(child)的结果分配给您要使用的字典键,构建一个类似于您的字典。看起来您希望使用标记作为键,并将值设置为具有该标记的项目列表,因此它将变为类似:

import collections
from lxml import etree

tree = etree.parse('file.xml')
root = tree.getroot()

def xml_to_dict(el):
    d={}
    if el.text:
        print '***write tag as string'
        d[el.tag] = el.text
    child_dicts = collections.defaultdict(list)
    for child in el.getchildren():
        child_dicts[child.tag].append(xml_to_dict(child))
    if child_dicts:
        d[el.tag] = child_dicts
    return d

xml_to_dict(root)

这将dict中的标记条目保留为defaultdict;如果由于某种原因需要普通字典,请使用d[el.tag] = dict(child_dicts)。请注意,与之前一样,如果标签同时包含文本和子项,则文本将不会出现在dict中。您可能想要为您的词典考虑不同的布局来应对。

修改

xml_to_dict中会产生输出的代码将不会在import collections from lxml import etree tree = etree.parse('file.xml') root = tree.getroot() def xml_to_item(el): if el.text: print '***write tag as string' item = el.text child_dicts = collections.defaultdict(list) for child in el.getchildren(): child_dicts[child.tag].append(xml_to_item(child)) return dict(child_dicts) or item def xml_to_dict(el): return {el.tag: xml_to_item(el)} print xml_to_dict(root) 中递归 - 因为您只需要外部元素的字典,而不是所有子标记。所以,你会使用类似的东西:

collections.defaultdict(list)

这仍然无法处理带有文本和子项的标签,并且它将***write tag as string ***write tag as string ***write tag as string ***write tag as string ***write tag as string ***write tag as string {'root': {'a': ['1', {'b': ['2', '2']}], 'aa': ['1a']}} 转换为普通字典,因此输出(几乎)正如您所期望的那样:

b

(如果你真的想要{{1}}标签中的文本数据的整数而不是字符串,你必须以某种方式明确地将它们变成整数。)

答案 1 :(得分:2)

简单:

from lxml import etree    
def recursive_dict(element):
    return element.tag, dict(map(recursive_dict, element)) or element.text

使用它:

   >> tree = etree.parse(file_name)
   >> recursive_dict(tree.getroot())
   ('root', {'tag1': text, 'tag2': subtag21: {tag211: text}})