Domanda

In realtà è una piccola cosa: ho questa funzione che converte gli oggetti dict in xml.

Ecco la funzione:

def dictToXml(d):
    from xml.sax.saxutils import escape

    def unicodify(o):
        if o is None:
            return u'';
        return unicode(o)

    lines = []
    def addDict(node, offset):
        for name, value in node.iteritems():
            if isinstance(value, dict):
                lines.append(offset + u"<%s>" % name)
                addDict(value, offset + u" " * 4)
                lines.append(offset + u"</%s>" % name)
            elif isinstance(value, list):
                for item in value:
                    if isinstance(item, dict):
                        lines.append(offset + u"<%s>" % name)
                        addDict(item, offset + u" " * 4)
                        lines.append(offset + u"</%s>" % name)
                    else:
                        lines.append(offset + u"<%s>%s</%s>" % (name, escape(unicodify(item)), name))
            else:
                lines.append(offset + u"<%s>%s</%s>" % (name, escape(unicodify(value)), name))

    addDict(d, u"")
    lines.append(u"")
    return u"\n".join(lines)

Ad esempio, converte questo dizionario

{ 'site': { 'name': 'stackoverflow', 'blogger': [ 'Jeff', 'Joel' ] } }

a:

<site>
    <name>stackoverflow</name>
    <blogger>jeff</blogger>
    <blogger>joel</blogger>
</site>

Funziona, ma la funzione addDict sembra un po 'troppo ripetitiva. Sono sicuro che c'è un modo per trasformarlo in 3 funzioni ricorsive denominate addList, addElse e offset +, ma il mio cervello è bloccato. Qualche aiuto?

Inoltre, qualsiasi modo per sbarazzarsi della cosa <=> in ogni riga sarebbe bello.

NOTA : ho scelto queste semantiche perché sto cercando di abbinare il comportamento di convertitore json-to-xml in org.json , che utilizzo in una parte diversa del mio progetto. Se sei arrivato a questa pagina solo cercando un convertitore da dizionario a xml, ci sono alcune opzioni davvero buone in alcune delle risposte. (Soprattutto pyfo ).

È stato utile?

Soluzione

Ho notato che hai in comune l'aggiunta di elementi. Usando questa comunanza, riforterei l'aggiunta di un oggetto a una funzione separata.

def addItem(item, name, offset):
          if isinstance(item, dict):
                lines.append(offset + u"<%s>" % name)
                addDict(item, offset + u" " * 4)
                lines.append(offset + u"</%s>" % name)
          else:
                lines.append(offset + u"<%s>%s</%s>" % (name, escape(unicodify(item)), name))

def addList(value,name, offset):
        for item in value:
            addItem(item, name, offset)

def addDict(node, offset):
        for name, value in node.iteritems():
            if isinstance(value, list):
                addList(value, name, offset)
            else:
                addItem(value, name, offset)

Avviso di avviso: questo codice non è testato o scritto da nessuno che utilizza effettivamente Python.

Altri suggerimenti

>>> from pyfo import pyfo
>>> d = ('site', { 'name': 'stackoverflow', 'blogger': [ 'Jeff', 'Joel' ] } )
>>> result = pyfo(d, pretty=True, prolog=True, encoding='ascii')
>>> print result.encode('ascii', 'xmlcharrefreplace')
<?xml version="1.0" encoding="ascii"?>
<site>
  <blogger>
    Jeff
    Joel
  </blogger>
  <name>stackoverflow</name>
</site>

Per installare pyfo :

$ easy_install pyfo

Per sbarazzarsi di " offset + " ;:

offset = 0
def addLine(str):
    lines.append(u" " * (offset * 4) + str

poi

...
    addLine(u"<%s>" % name)
    offset = offset + 1
    addDict(value)
    offset = offset - 1
    addLine(u"</%s>" % name)

Non hai accesso a un interprete qui, quindi prendi questo con un pizzico di sale :(

Il tuo codice originale produce XML non valido e può produrre lo stesso XML per due dizionari diversi (non è iniettivo , parlando matematicamente).

Ad esempio, se si dispone di un elenco come valore dell'unica chiave in un dizionario:

 d = { 'list': [1,2,3] }

Mi aspetto che il tuo codice produca

 <list>1</list><list>2</list><list>3</list>

e non esiste alcun elemento radice. Qualsiasi XML dovrebbe avere un solo e un solo elemento radice.

Quindi dato l'XML prodotto dal tuo codice, è impossibile dire se questo XML

 <tag>1</tag>

è stato prodotto da { 'tag': 1 } o da { 'tag': [1] }.

Quindi, suggerisco

  • inizia sempre dall'elemento radice
  • rappresentano elenchi con due tag speciali (ad esempio <list/> e <item/>) o contrassegnali come tali negli attributi

Quindi, dopo le decisioni su queste carenze concettuali , possiamo generare XML corretti e inequivocabili. Ho scelto di utilizzare gli attributi per contrassegnare gli elenchi e ho usato ElementTree per costruire automaticamente l'albero XML. Inoltre, la ricorsione aiuta (add_value_to_xml si chiama ricorsivamente):

from xml.etree.ElementTree import Element, SubElement, tostring

def is_scalar(v):
    return isinstance(v,basestring) or isinstance(v,float) \
        or isinstance(v,int) or isinstance(v,bool)

def add_value_to_xml(root,v):
    if type(v) == type({}):
        for k,kv in v.iteritems():
            vx = SubElement(root,unicode(k))
            vx = add_value_to_xml(vx,kv)
    elif type(v) == list:
        root.set('type','list')
        for e in v:
            li = SubElement(root,root.tag)
            li = add_value_to_xml(li,e)
            li.set('type','item')
    elif is_scalar(v):
        root.text = unicode(v)
    else:
        raise Exception("add_value_to_xml: unsuppoted type (%s)"%type(v))
    return root

def dict_to_xml(d,root='dict'):
    x = Element(root)
    x = add_value_to_xml(x,d)
    return x

d = { 'float': 5194.177, 'str': 'eggs', 'int': 42,
        'list': [1,2], 'dict': { 'recursion': True } }
x = dict_to_xml(d)
print tostring(x)

Il risultato della conversione del test dict è:

<dict><int>42</int><dict><recursion>True</recursion></dict><float>5194.177</float><list type="list"><list type="item">1</list><list type="item">2</list></list><str>eggs</str></dict>

Ecco il mio breve schizzo per una soluzione: hanno una funzione generale addSomething() che invia in base al tipo di valore in addDict(), addList() o addElse(). Queste funzioni richiamano ricorsivamente if di nuovo.

Fondamentalmente stai prendendo in considerazione le parti nella clausola <=> e aggiungi una chiamata ricorsiva.

Ecco cosa trovo utile quando si lavora con XML. In realtà, crea prima la struttura del nodo XML, quindi esegui il rendering in un secondo testo.

Questo separa due preoccupazioni non correlate.

  1. Come posso trasformare la mia struttura Python in un modello a oggetti XML?

  2. Come formattare quel modello a oggetti XML?

È difficile quando metti insieme queste due cose in un'unica funzione. Se, d'altra parte, li separi, allora hai due cose. Innanzitutto, hai una funzione notevolmente più semplice di & Quot; walk & Quot; la tua struttura Python e restituisci un nodo XML. I tuoi nodi XML possono essere resi in testo con alcune regole di codifica e formattazione preferite applicate.

from xml.sax.saxutils import escape

class Node( object ):
    def __init__( self, name, *children ):
        self.name= name
        self.children= children
    def toXml( self, indent ):
        if len(self.children) == 0:
            return u"%s<%s/>" % ( indent*4*u' ', self.name )
        elif len(self.children) == 1:
            child= self.children[0].toXml(0)
            return u"%s<%s>%s</%s>" % ( indent*4*u' ', self.name, child, self.name )
        else:
            items = [ u"%s<%s>" % ( indent*4*u' ', self.name ) ]
            items.extend( [ c.toXml(indent+1) for c in self.children ] )
            items.append( u"%s</%s>" % ( indent*4*u' ', self.name ) )
            return u"\n".join( items )

class Text( Node ):
    def __init__( self, value ):
        self.value= value
    def toXml( self, indent ):
        def unicodify(o):
            if o is None:
                return u'';
            return unicode(o)
        return "%s%s" % ( indent*4*u' ', escape( unicodify(self.value) ), )

def dictToXml(d):

    def dictToNodeList(node):
        nodes= []
        for name, value in node.iteritems():
            if isinstance(value, dict):
                n= Node( name, *dictToNodeList( value ) )
                nodes.append( n )
            elif isinstance(value, list):
                for item in value:
                    if isinstance(item, dict):
                        n= Node( name, *dictToNodeList( value ) )
                        nodes.append( n )
                    else:
                        n= Node( name, Text( item ) )
                        nodes.append( n )
            else:
                n= Node( name, Text( value ) )
                nodes.append( n )
        return nodes

    return u"\n".join( [ n.toXml(0) for n in dictToNodeList(d) ] )
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top