如何映射到字典而不是列表?
我有一个函数,它的基本功能是把一个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 个回答
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}})
编辑:问题示例的输出结果:
('root', {'a': {'b': '2'}, 'aa': '1a'})
看起来etree会跳过重复的元素。
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)
这样的话,字典中的标签条目会变成一个默认字典;如果你出于某种原因想要一个普通的字典,可以用 d[el.tag] = dict(child_dicts)
。需要注意的是,像之前提到的,如果一个标签同时有文本和子元素,文本不会出现在字典里。你可能需要考虑一下字典的不同布局来处理这个问题。
编辑:
生成你重新表述的问题中输出的代码不会在 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
标签中的文本数据是整数而不是字符串,你需要以某种方式明确地将它们转换为整数。)