Comment convertir une chaîne XML en dictionnaire?

125

J'ai un programme qui lit un document xml à partir d'une socket. J'ai le document xml stocké dans une chaîne que je voudrais convertir directement en dictionnaire Python, de la même manière que dans Djangosimplejson bibliothèque .

Prenons comme exemple:

str ="<?xml version="1.0" ?><person><name>john</name><age>20</age></person"
dic_xml = convert_to_dic(str)

Alors dic_xmlressemblerait à{'person' : { 'name' : 'john', 'age' : 20 } }

utilisateur361526
la source
str a quelques erreurs de syntaxe. try: str = '<? xml version = "1.0"?> <person> <name> john </name> <age> 20 </age> </person>'
Keir

Réponses:

58

C'est un excellent module que quelqu'un a créé. Je l'ai utilisé plusieurs fois. http://code.activestate.com/recipes/410469-xml-as-dictionary/

Voici le code du site Web au cas où le lien ne fonctionne pas.

from xml.etree import cElementTree as ElementTree

class XmlListConfig(list):
    def __init__(self, aList):
        for element in aList:
            if element:
                # treat like dict
                if len(element) == 1 or element[0].tag != element[1].tag:
                    self.append(XmlDictConfig(element))
                # treat like list
                elif element[0].tag == element[1].tag:
                    self.append(XmlListConfig(element))
            elif element.text:
                text = element.text.strip()
                if text:
                    self.append(text)


class XmlDictConfig(dict):
    '''
    Example usage:

    >>> tree = ElementTree.parse('your_file.xml')
    >>> root = tree.getroot()
    >>> xmldict = XmlDictConfig(root)

    Or, if you want to use an XML string:

    >>> root = ElementTree.XML(xml_string)
    >>> xmldict = XmlDictConfig(root)

    And then use xmldict for what it is... a dict.
    '''
    def __init__(self, parent_element):
        if parent_element.items():
            self.update(dict(parent_element.items()))
        for element in parent_element:
            if element:
                # treat like dict - we assume that if the first two tags
                # in a series are different, then they are all different.
                if len(element) == 1 or element[0].tag != element[1].tag:
                    aDict = XmlDictConfig(element)
                # treat like list - we assume that if the first two tags
                # in a series are the same, then the rest are the same.
                else:
                    # here, we put the list in dictionary; the key is the
                    # tag name the list elements all share in common, and
                    # the value is the list itself 
                    aDict = {element[0].tag: XmlListConfig(element)}
                # if the tag has attributes, add those to the dict
                if element.items():
                    aDict.update(dict(element.items()))
                self.update({element.tag: aDict})
            # this assumes that if you've got an attribute in a tag,
            # you won't be having any text. This may or may not be a 
            # good idea -- time will tell. It works for the way we are
            # currently doing XML configuration files...
            elif element.items():
                self.update({element.tag: dict(element.items())})
            # finally, if there are no child tags and no attributes, extract
            # the text
            else:
                self.update({element.tag: element.text})

Exemple d'utilisation:

tree = ElementTree.parse('your_file.xml')
root = tree.getroot()
xmldict = XmlDictConfig(root)

// Ou, si vous souhaitez utiliser une chaîne XML:

root = ElementTree.XML(xml_string)
xmldict = XmlDictConfig(root)
James
la source
4
Vous pouvez utiliser 'xmltodict' alternativement
mrash
7
J'ai essayé cela et c'est beaucoup plus rapide que xmltodict. Pour analyser un fichier xml de 80 Mo, il a fallu 7 s, avec xmltodict, il a fallu 90 s
Eddy
1
Confirmé ... Je n'ai pas testé cela contre tous les cas de bord, mais pour mes chaînes XML plutôt simples, c'est assez rapide (environ 8 fois plus rapide que la xmltodictbibliothèque). L'inconvénient est que vous devez l'héberger vous-même au sein de votre projet.
Dirk
10
Salut, cela fonctionne parfaitement, ajoutera juste un extrait pour ceux qui ne peuvent pas trouver cElementTree, changez simplement la première ligne en: from xml.etree import cElementTree as ElementTree
Rafael Aguilar
2
Vote à la baisse car il y a de meilleures réponses affichées ci-dessous, en particulier dans la gestion de plusieurs balises avec le même nom.
Maksym
280

xmltodict (divulgation complète: je l'ai écrit) fait exactement cela:

xmltodict.parse("""
<?xml version="1.0" ?>
<person>
  <name>john</name>
  <age>20</age>
</person>""")
# {u'person': {u'age': u'20', u'name': u'john'}}
Martin Blech
la source
22
C'est un module fantastique.
zekel
2
vous venez de m'économiser beaucoup d'efforts. Fait ma journée.
LRE
3
aussi, pour les futurs googlenauts - j'ai pu l'utiliser dans App Engine, qui, à mon avis, ne fonctionnait pas bien avec la plupart des bibliothèques XML en Python.
LRE
2
Le u indique simplement qu'il s'agit d'une chaîne unicode stockée. Cela n'affecte en aucun cas la valeur de la chaîne.
Joshua Olson
2
Agréable. Et oui, @ypercube, il existe une fonction xmldict.unparse () pour l'inverse.
Duther le
47

L'extrait de code XML-to-Python-dict suivant analyse les entités ainsi que les attributs suivant cette "spécification" XML-to-JSON . C'est la solution la plus générale pour tous les cas de XML.

from collections import defaultdict

def etree_to_dict(t):
    d = {t.tag: {} if t.attrib else None}
    children = list(t)
    if children:
        dd = defaultdict(list)
        for dc in map(etree_to_dict, children):
            for k, v in dc.items():
                dd[k].append(v)
        d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.items()}}
    if t.attrib:
        d[t.tag].update(('@' + k, v) for k, v in t.attrib.items())
    if t.text:
        text = t.text.strip()
        if children or t.attrib:
            if text:
              d[t.tag]['#text'] = text
        else:
            d[t.tag] = text
    return d

C'est utilisé:

from xml.etree import cElementTree as ET
e = ET.XML('''
<root>
  <e />
  <e>text</e>
  <e name="value" />
  <e name="value">text</e>
  <e> <a>text</a> <b>text</b> </e>
  <e> <a>text</a> <a>text</a> </e>
  <e> text <a>text</a> </e>
</root>
''')

from pprint import pprint
pprint(etree_to_dict(e))

La sortie de cet exemple (selon la "spécification" liée ci-dessus) doit être:

{'root': {'e': [None,
                'text',
                {'@name': 'value'},
                {'#text': 'text', '@name': 'value'},
                {'a': 'text', 'b': 'text'},
                {'a': ['text', 'text']},
                {'#text': 'text', 'a': 'text'}]}}

Pas nécessairement joli, mais c'est sans ambiguïté, et des entrées XML plus simples se traduisent par un JSON plus simple. :)


Mettre à jour

Si vous voulez faire l' inverse , émettre une chaîne XML à partir d'un JSON / dict , vous pouvez utiliser:

try:
  basestring
except NameError:  # python3
  basestring = str

def dict_to_etree(d):
    def _to_etree(d, root):
        if not d:
            pass
        elif isinstance(d, basestring):
            root.text = d
        elif isinstance(d, dict):
            for k,v in d.items():
                assert isinstance(k, basestring)
                if k.startswith('#'):
                    assert k == '#text' and isinstance(v, basestring)
                    root.text = v
                elif k.startswith('@'):
                    assert isinstance(v, basestring)
                    root.set(k[1:], v)
                elif isinstance(v, list):
                    for e in v:
                        _to_etree(e, ET.SubElement(root, k))
                else:
                    _to_etree(v, ET.SubElement(root, k))
        else:
            raise TypeError('invalid type: ' + str(type(d)))
    assert isinstance(d, dict) and len(d) == 1
    tag, body = next(iter(d.items()))
    node = ET.Element(tag)
    _to_etree(body, node)
    return ET.tostring(node)

pprint(dict_to_etree(d))
K3 --- rnc
la source
1
Thx pour ce code! Informations supplémentaires: si vous utilisez python 2.5, vous ne pouvez pas utiliser la compréhension du dictionnaire, vous devez donc changer la ligne d = {t.tag: {k:v[0] if len(v) == 1 else v for k, v in dd.iteritems()}} en d = { t.tag: dict( (k, v[0] if len(v) == 1 else v) for k, v in dd.iteritems() ) }
M--
2
J'ai testé près de 10 extraits / modules python / etc. pour cela. Celui-ci est le meilleur que j'ai trouvé. D'après mes tests, il est: 1) beaucoup plus rapide que github.com/martinblech/xmltodict (basé sur l'api XML SAX) 2) mieux que github.com/mcspring/XML2Dict qui a quelques petits problèmes lorsque plusieurs enfants ont les mêmes noms 3 ) mieux que code.activestate.com/recipes/410469-xml-as-dictionary qui avait aussi de petits problèmes et plus important: 4) un code beaucoup plus court que tous les précédents! Thanks @ K3 --- rnc
Basj
C'est, de loin, la réponse la plus complète, et cela fonctionne sur> 2.6, et c'est assez flexible. mon seul problème est que le texte peut changer où il réside selon qu'il existe un attribut ou non) J'ai également publié une solution encore plus petite et plus rigide.
Erik Aronesty
1
Si vous avez besoin d'obtenir un dict ordonné à partir d'un fichier XML, veuillez utiliser ce même exemple avec quelques modifications (voir ma réponse ci-dessous): stackoverflow.com/questions/2148119
...
C'est aussi assez astucieux et rapide lorsqu'il est utilisé avec cElementTreeou lxml.etree. Notez que lors de l'utilisation de Python 3, tous .iteritems()doivent être modifiés en .items()(même comportement mais le mot-clé est passé de Python 2 à 3).
Dirk
25

Cette version légère, bien que non configurable, est assez facile à personnaliser selon les besoins et fonctionne dans les anciens pythons. De plus, il est rigide - ce qui signifie que les résultats sont les mêmes quelle que soit l'existence des attributs.

import xml.etree.ElementTree as ET

from copy import copy

def dictify(r,root=True):
    if root:
        return {r.tag : dictify(r, False)}
    d=copy(r.attrib)
    if r.text:
        d["_text"]=r.text
    for x in r.findall("./*"):
        if x.tag not in d:
            d[x.tag]=[]
        d[x.tag].append(dictify(x,False))
    return d

Alors:

root = ET.fromstring("<erik><a x='1'>v</a><a y='2'>w</a></erik>")

dictify(root)

Résulte en:

{'erik': {'a': [{'x': '1', '_text': 'v'}, {'y': '2', '_text': 'w'}]}}
Erik Aronesty
la source
2
J'aime cette solution. Simple et ne nécessite pas de bibliothèques externes.
MattK
6

Les versions les plus récentes des bibliothèques PicklingTools (1.3.0 et 1.3.1) prennent en charge les outils de conversion de XML en dict Python.

Le téléchargement est disponible ici: PicklingTools 1.3.1

Il y a pas mal de documentation pour les convertisseurs ici : la documentation décrit en détail toutes les décisions et les problèmes qui se poseront lors de la conversion entre les dictionnaires XML et Python (il existe un certain nombre de cas extrêmes: attributs, listes, listes anonymes, anonymes dicts, eval, etc. que la plupart des convertisseurs ne gèrent pas). En général, cependant, les convertisseurs sont faciles à utiliser. Si un 'example.xml' contient:

<top>
  <a>1</a>
  <b>2.2</b>
  <c>three</c>
</top>

Ensuite, pour le convertir en dictionnaire:

>>> from xmlloader import *
>>> example = file('example.xml', 'r')   # A document containing XML
>>> xl = StreamXMLLoader(example, 0)     # 0 = all defaults on operation
>>> result = xl.expect XML()
>>> print result
{'top': {'a': '1', 'c': 'three', 'b': '2.2'}}

Il existe des outils de conversion à la fois en C ++ et Python: le C ++ et Python effectuent une conversion identique, mais le C ++ est environ 60 fois plus rapide

rts1
la source
bien sûr, alors s'il y a 2 a, ce n'est pas un bon format.
Erik Aronesty
1
Cela semble intéressant, mais je n'ai pas encore compris comment les PicklingTools sont censés être utilisés - est-ce juste une archive de fichiers de code source à partir de laquelle je dois trouver les bons pour mon travail, puis les copier dans mon projet? Aucun module à charger ou quelque chose de plus simple?
Dirk
J'obtiens : dans peekIntoNextNWSChar c = self.is .read (1) AttributeError: l'objet 'str' n'a pas d'attribut 'read'
sqp_125
5

Vous pouvez le faire assez facilement avec lxml. Installez-le d'abord:

[sudo] pip install lxml

Voici une fonction récursive que j'ai écrite et qui fait le gros du travail pour vous:

from lxml import objectify as xml_objectify


def xml_to_dict(xml_str):
    """ Convert xml to dict, using lxml v3.4.2 xml processing library """
    def xml_to_dict_recursion(xml_object):
        dict_object = xml_object.__dict__
        if not dict_object:
            return xml_object
        for key, value in dict_object.items():
            dict_object[key] = xml_to_dict_recursion(value)
        return dict_object
    return xml_to_dict_recursion(xml_objectify.fromstring(xml_str))

xml_string = """<?xml version="1.0" encoding="UTF-8"?><Response><NewOrderResp>
<IndustryType>Test</IndustryType><SomeData><SomeNestedData1>1234</SomeNestedData1>
<SomeNestedData2>3455</SomeNestedData2></SomeData></NewOrderResp></Response>"""

print xml_to_dict(xml_string)

La variante ci-dessous préserve la clé / l'élément parent:

def xml_to_dict(xml_str):
    """ Convert xml to dict, using lxml v3.4.2 xml processing library, see http://lxml.de/ """
    def xml_to_dict_recursion(xml_object):
        dict_object = xml_object.__dict__
        if not dict_object:  # if empty dict returned
            return xml_object
        for key, value in dict_object.items():
            dict_object[key] = xml_to_dict_recursion(value)
        return dict_object
    xml_obj = objectify.fromstring(xml_str)
    return {xml_obj.tag: xml_to_dict_recursion(xml_obj)}

Si vous souhaitez uniquement renvoyer un sous-arbre et le convertir en dict, vous pouvez utiliser Element.find () pour obtenir le sous-arbre, puis le convertir:

xml_obj.find('.//')  # lxml.objectify.ObjectifiedElement instance

Consultez la documentation lxml ici . J'espère que ça aide!

Radtek
la source
5

Avertissement: Cet analyseur XML modifié a été inspiré par Adam Clark L'analyseur XML original fonctionne pour la plupart des cas simples. Cependant, cela n'a pas fonctionné pour certains fichiers XML complexes. J'ai débogué le code ligne par ligne et j'ai finalement résolu quelques problèmes. Si vous trouvez des bugs, faites-le moi savoir. Je suis heureux de le réparer.

class XmlDictConfig(dict):  
    '''   
    Note: need to add a root into if no exising    
    Example usage:
    >>> tree = ElementTree.parse('your_file.xml')
    >>> root = tree.getroot()
    >>> xmldict = XmlDictConfig(root)
    Or, if you want to use an XML string:
    >>> root = ElementTree.XML(xml_string)
    >>> xmldict = XmlDictConfig(root)
    And then use xmldict for what it is... a dict.
    '''
    def __init__(self, parent_element):
        if parent_element.items():
            self.updateShim( dict(parent_element.items()) )
        for element in parent_element:
            if len(element):
                aDict = XmlDictConfig(element)
            #   if element.items():
            #   aDict.updateShim(dict(element.items()))
                self.updateShim({element.tag: aDict})
            elif element.items():    # items() is specialy for attribtes
                elementattrib= element.items()
                if element.text:           
                    elementattrib.append((element.tag,element.text ))     # add tag:text if there exist
                self.updateShim({element.tag: dict(elementattrib)})
            else:
                self.updateShim({element.tag: element.text})

    def updateShim (self, aDict ):
        for key in aDict.keys():   # keys() includes tag and attributes
            if key in self:
                value = self.pop(key)
                if type(value) is not list:
                    listOfDicts = []
                    listOfDicts.append(value)
                    listOfDicts.append(aDict[key])
                    self.update({key: listOfDicts})
                else:
                    value.append(aDict[key])
                    self.update({key: value})
            else:
                self.update({key:aDict[key]})  # it was self.update(aDict)    
tigre
la source
3
def xml_to_dict(node):
    u''' 
    @param node:lxml_node
    @return: dict 
    '''

    return {'tag': node.tag, 'text': node.text, 'attrib': node.attrib, 'children': {child.tag: xml_to_dict(child) for child in node}}
dibrovsd
la source
2

L'analyseur XML le plus simple à utiliser pour Python est ElementTree (à partir de 2.5x, il se trouve dans la bibliothèque standard xml.etree.ElementTree). Je ne pense pas qu'il y ait quoi que ce soit qui fasse exactement ce que vous voulez hors de la boîte. Ce serait assez trivial d'écrire quelque chose pour faire ce que vous voulez en utilisant ElementTree, mais pourquoi convertir en dictionnaire, et pourquoi pas simplement utiliser ElementTree directement.


la source
2

Le code de http://code.activestate.com/recipes/410469-xml-as-dictionary/ fonctionne bien, mais s'il y a plusieurs éléments qui sont identiques à un endroit donné dans la hiérarchie, il les remplace simplement.

J'ai ajouté un shim entre qui regarde pour voir si l'élément existe déjà avant self.update (). Si tel est le cas, fait apparaître l'entrée existante et crée une liste à partir de l'existant et du nouveau. Tous les doublons ultérieurs sont ajoutés à la liste.

Je ne sais pas si cela peut être géré plus gracieusement, mais cela fonctionne:

import xml.etree.ElementTree as ElementTree

class XmlDictConfig(dict):
    def __init__(self, parent_element):
        if parent_element.items():
            self.updateShim(dict(parent_element.items()))
        for element in parent_element:
            if len(element):
                aDict = XmlDictConfig(element)
                if element.items():
                    aDict.updateShim(dict(element.items()))
                self.updateShim({element.tag: aDict})
            elif element.items():
                self.updateShim({element.tag: dict(element.items())})
            else:
                self.updateShim({element.tag: element.text.strip()})

    def updateShim (self, aDict ):
        for key in aDict.keys():
            if key in self:
                value = self.pop(key)
                if type(value) is not list:
                    listOfDicts = []
                    listOfDicts.append(value)
                    listOfDicts.append(aDict[key])
                    self.update({key: listOfDicts})

                else:
                    value.append(aDict[key])
                    self.update({key: value})
            else:
                self.update(aDict)
Adam Clark
la source
2

De @ K3 --- réponse rnc (le meilleur pour moi), j'ai ajouté de petites modifications pour obtenir un OrderedDict à partir d'un texte XML (parfois, l'ordre est important):

def etree_to_ordereddict(t):
d = OrderedDict()
d[t.tag] = OrderedDict() if t.attrib else None
children = list(t)
if children:
    dd = OrderedDict()
    for dc in map(etree_to_ordereddict, children):
        for k, v in dc.iteritems():
            if k not in dd:
                dd[k] = list()
            dd[k].append(v)
    d = OrderedDict()
    d[t.tag] = OrderedDict()
    for k, v in dd.iteritems():
        if len(v) == 1:
            d[t.tag][k] = v[0]
        else:
            d[t.tag][k] = v
if t.attrib:
    d[t.tag].update(('@' + k, v) for k, v in t.attrib.iteritems())
if t.text:
    text = t.text.strip()
    if children or t.attrib:
        if text:
            d[t.tag]['#text'] = text
    else:
        d[t.tag] = text
return d

En suivant l'exemple @ K3 --- rnc, vous pouvez l'utiliser:

from xml.etree import cElementTree as ET
e = ET.XML('''
<root>
  <e />
  <e>text</e>
  <e name="value" />
  <e name="value">text</e>
  <e> <a>text</a> <b>text</b> </e>
  <e> <a>text</a> <a>text</a> </e>
  <e> text <a>text</a> </e>
</root>
''')

from pprint import pprint
pprint(etree_to_ordereddict(e))

J'espère que ça aide ;)

serfer2
la source
1

Voici un lien vers une solution ActiveState - et le code au cas où il disparaîtrait à nouveau.

==================================================
xmlreader.py:
==================================================
from xml.dom.minidom import parse


class NotTextNodeError:
    pass


def getTextFromNode(node):
    """
    scans through all children of node and gathers the
    text. if node has non-text child-nodes, then
    NotTextNodeError is raised.
    """
    t = ""
    for n in node.childNodes:
    if n.nodeType == n.TEXT_NODE:
        t += n.nodeValue
    else:
        raise NotTextNodeError
    return t


def nodeToDic(node):
    """
    nodeToDic() scans through the children of node and makes a
    dictionary from the content.
    three cases are differentiated:
    - if the node contains no other nodes, it is a text-node
    and {nodeName:text} is merged into the dictionary.
    - if the node has the attribute "method" set to "true",
    then it's children will be appended to a list and this
    list is merged to the dictionary in the form: {nodeName:list}.
    - else, nodeToDic() will call itself recursively on
    the nodes children (merging {nodeName:nodeToDic()} to
    the dictionary).
    """
    dic = {} 
    for n in node.childNodes:
    if n.nodeType != n.ELEMENT_NODE:
        continue
    if n.getAttribute("multiple") == "true":
        # node with multiple children:
        # put them in a list
        l = []
        for c in n.childNodes:
            if c.nodeType != n.ELEMENT_NODE:
            continue
        l.append(nodeToDic(c))
            dic.update({n.nodeName:l})
        continue

    try:
        text = getTextFromNode(n)
    except NotTextNodeError:
            # 'normal' node
            dic.update({n.nodeName:nodeToDic(n)})
            continue

        # text node
        dic.update({n.nodeName:text})
    continue
    return dic


def readConfig(filename):
    dom = parse(filename)
    return nodeToDic(dom)





def test():
    dic = readConfig("sample.xml")

    print dic["Config"]["Name"]
    print
    for item in dic["Config"]["Items"]:
    print "Item's Name:", item["Name"]
    print "Item's Value:", item["Value"]

test()



==================================================
sample.xml:
==================================================
<?xml version="1.0" encoding="UTF-8"?>

<Config>
    <Name>My Config File</Name>

    <Items multiple="true">
    <Item>
        <Name>First Item</Name>
        <Value>Value 1</Value>
    </Item>
    <Item>
        <Name>Second Item</Name>
        <Value>Value 2</Value>
    </Item>
    </Items>

</Config>



==================================================
output:
==================================================
My Config File

Item's Name: First Item
Item's Value: Value 1
Item's Name: Second Item
Item's Value: Value 2
tgray
la source
Oui, ça l'est. J'ai reproduit le code ici au cas où il recommencerait.
Jamie Bull
0

À un moment donné, j'ai dû analyser et écrire du XML qui ne se composait que d'éléments sans attributs, donc un mappage 1: 1 de XML à dict était possible facilement. Voici ce que j'ai proposé au cas où quelqu'un d'autre n'aurait pas besoin d'attributs:

def xmltodict(element):
    if not isinstance(element, ElementTree.Element):
        raise ValueError("must pass xml.etree.ElementTree.Element object")

    def xmltodict_handler(parent_element):
        result = dict()
        for element in parent_element:
            if len(element):
                obj = xmltodict_handler(element)
            else:
                obj = element.text

            if result.get(element.tag):
                if hasattr(result[element.tag], "append"):
                    result[element.tag].append(obj)
                else:
                    result[element.tag] = [result[element.tag], obj]
            else:
                result[element.tag] = obj
        return result

    return {element.tag: xmltodict_handler(element)}


def dicttoxml(element):
    if not isinstance(element, dict):
        raise ValueError("must pass dict type")
    if len(element) != 1:
        raise ValueError("dict must have exactly one root key")

    def dicttoxml_handler(result, key, value):
        if isinstance(value, list):
            for e in value:
                dicttoxml_handler(result, key, e)
        elif isinstance(value, basestring):
            elem = ElementTree.Element(key)
            elem.text = value
            result.append(elem)
        elif isinstance(value, int) or isinstance(value, float):
            elem = ElementTree.Element(key)
            elem.text = str(value)
            result.append(elem)
        elif value is None:
            result.append(ElementTree.Element(key))
        else:
            res = ElementTree.Element(key)
            for k, v in value.items():
                dicttoxml_handler(res, k, v)
            result.append(res)

    result = ElementTree.Element(element.keys()[0])
    for key, value in element[element.keys()[0]].items():
        dicttoxml_handler(result, key, value)
    return result

def xmlfiletodict(filename):
    return xmltodict(ElementTree.parse(filename).getroot())

def dicttoxmlfile(element, filename):
    ElementTree.ElementTree(dicttoxml(element)).write(filename)

def xmlstringtodict(xmlstring):
    return xmltodict(ElementTree.fromstring(xmlstring).getroot())

def dicttoxmlstring(element):
    return ElementTree.tostring(dicttoxml(element))
Josch
la source
0

@dibrovsd: La solution ne fonctionnera pas si le xml a plus d'une balise avec le même nom

Dans votre ligne de pensée, j'ai un peu modifié le code et l'ai écrit pour le nœud général au lieu de la racine:

from collections import defaultdict
def xml2dict(node):
    d, count = defaultdict(list), 1
    for i in node:
        d[i.tag + "_" + str(count)]['text'] = i.findtext('.')[0]
        d[i.tag + "_" + str(count)]['attrib'] = i.attrib # attrib gives the list
        d[i.tag + "_" + str(count)]['children'] = xml2dict(i) # it gives dict
     return d
pg2455
la source
0

J'ai modifié l'une des réponses à mon goût et pour travailler avec plusieurs valeurs avec la même balise, par exemple, considérez le code xml suivant enregistré dans le fichier XML.xml

     <A>
        <B>
            <BB>inAB</BB>
            <C>
                <D>
                    <E>
                        inABCDE
                    </E>
                    <E>value2</E>
                    <E>value3</E>
                </D>
                <inCout-ofD>123</inCout-ofD>
            </C>
        </B>
        <B>abc</B>
        <F>F</F>
    </A>

et en python

import xml.etree.ElementTree as ET




class XMLToDictionary(dict):
    def __init__(self, parentElement):
        self.parentElement = parentElement
        for child in list(parentElement):
            child.text = child.text if (child.text != None) else  ' '
            if len(child) == 0:
                self.update(self._addToDict(key= child.tag, value = child.text.strip(), dict = self))
            else:
                innerChild = XMLToDictionary(parentElement=child)
                self.update(self._addToDict(key=innerChild.parentElement.tag, value=innerChild, dict=self))

    def getDict(self):
        return {self.parentElement.tag: self}

    class _addToDict(dict):
        def __init__(self, key, value, dict):
            if not key in dict:
                self.update({key: value})
            else:
                identical = dict[key] if type(dict[key]) == list else [dict[key]]
                self.update({key: identical + [value]})


tree = ET.parse('./XML.xml')
root = tree.getroot()
parseredDict = XMLToDictionary(root).getDict()
print(parseredDict)

la sortie est

{'A': {'B': [{'BB': 'inAB', 'C': {'D': {'E': ['inABCDE', 'value2', 'value3']}, 'inCout-ofD': '123'}}, 'abc'], 'F': 'F'}}
codeur
la source
-2

J'ai une méthode récursive pour obtenir un dictionnaire à partir d'un élément lxml

    def recursive_dict(element):
        return (element.tag.split('}')[1],
                dict(map(recursive_dict, element.getchildren()),
                     **element.attrib))
moylop260
la source
1
Il manque du code à cette solution, comme l'importation et la configuration. J'ai reçu le message 'str' object has no attribute 'tag'
Chris Nielsen