代码之家  ›  专栏  ›  技术社区  ›  significance

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

  •  1
  • significance  · 技术社区  · 15 年前

    我有以下函数,它是将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_重写为_dict(el),以便获得所需的输出?

    为了清晰起见,这里是我要分析的XML。

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

    谢谢:)

    2 回复  |  直到 11 年前
        1
  •  5
  •   Thomas Wouters    15 年前

    好, 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中的标记条目保留为默认dict;如果出于某种原因需要常规dict,请使用 d[el.tag] = dict(child_dicts) . 请注意,和以前一样,如果标记同时包含文本和子项,则文本不会出现在听写中。您可能需要考虑不同的布局,以便听写能够处理此问题。

    编辑:

    在重新表述的问题中产生输出的代码不会在 xml_to_dict --因为您只需要一个外部元素的dict,而不是所有子标记的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 标记,您必须以某种方式将它们显式地转换为整数。)

        2
  •  2
  •   Samoht    11 年前

    更简单:

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