2010-11-06 4 views
1

i ont la fonction suivante, qui doe un travail de base de la cartographie un objet lxml à un dictionnaire ...comment mapper vers un dictionnaire plutôt qu'une liste?

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) 

au moment où il me donne ....

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

mais je aimerait ....

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

comment puis-je réécris la fonction xml_to_dict (el) de sorte que je reçois la sortie nécessaire?

Voici le xml que je suis en train d'analyser, pour plus de clarté.

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

merci :)

+3

L'utilisation d'un dictionnaire ne permettra sur l'élément avec la clé a ou b comment voulez-vous que - à-dire mieux pour montrer ce que votre puissance requise est – Mark

+0

ont reformulé la question à inclure nécessaire sortie - merci Mark – significance

Répondre

5

Eh bien, map() renvoie toujours une liste, donc la réponse facile est "ne pas utiliser map()". Au lieu de cela, créez un dictionnaire comme vous le faites déjà en bouclant children et en affectant le résultat de xml_to_dict(child) à la clé du dictionnaire que vous souhaitez utiliser. On dirait que vous voulez utiliser la balise comme la clé et que la valeur soit une liste d'éléments avec cette balise, il deviendrait quelque chose comme:

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) 

Cela laisse l'entrée de balise dans le dict comme defaultdict ; Si vous voulez une dict normale pour une raison quelconque, utilisez d[el.tag] = dict(child_dicts). Notez que, comme précédemment, si une balise contient à la fois du texte et des enfants, le texte n'apparaîtra pas dans la dict. Vous voudrez peut-être penser à une mise en page différente pour votre dict pour y faire face.

EDIT:

code

qui produirait la sortie dans votre question reformulée ne récursivité dans xml_to_dict - parce que vous voulez seulement un dict pour l'élément extérieur, pas pour tous les balises enfants. Donc, vous devez utiliser quelque chose comme:

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) 

Cela ne gère pas encore les étiquettes à la fois le texte et les enfants sanely, et il fait tourner les collections.defaultdict(list) dans une dict normale de sorte que la sortie est (presque) comme prévu:

***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']}} 

(. Si vous voulez vraiment entiers au lieu de chaînes pour les données textuelles dans les b balises, vous devez les transformer explicitement en entiers en quelque sorte)

+0

désolé thomas, c'est à peu près une réponse correcte à ma question initiale, mais malheureusement, je n'étais pas assez précis la première fois. J'ai reformulé la question - j'espère que vous pouvez aider. désolé pour le problème ... – significance

+0

parfait - merci beaucoup de me répondre deux fois !!! – significance

+0

thomas - désolé de soulever cela à nouveau, mais je rencontre des problèmes pour mettre en place collections.OrderedDict plutôt que collections.defaultdict - j'espère que vous pouvez aider! Merci beaucoup :) – significance

2

Simpler:

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

Pour l'utiliser:

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