Как мне сопоставить словарь, а не список? - PullRequest
1 голос
/ 06 ноября 2010

У меня есть следующая функция, которая выполняет основную работу по отображению объекта 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 ]

5 голосов
/ 06 ноября 2010

Ну, 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, используйте d[el.tag] = dict(child_dicts).Обратите внимание, что, как и раньше, если у тега есть и текст, и дочерние элементы, текст не будет отображаться в dict.Возможно, вы захотите придумать другой макет, чтобы ваш диктант справился с этим.

РЕДАКТИРОВАТЬ:

Код, который будет выводить результат в вашем перефразированном вопросе, не будетrecurse in xml_to_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) в нормальный dict, так что вывод (почти)как вы ожидаете:

***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 голосов
/ 29 августа 2014

Проще:

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