0

I have python dictionaries with the following structure:

d={ 'cfdi:Emisor': {'rfc': u'ALF040329CX6', 'nombre': u'ALFATECH, S.A. DE C.V.', 
     'cfdi:RegimenFiscal': {'Regimen': u'Personas morales del r\xe9gimen general'}, 
      'cfdi:ExpedidoEn': {'calle': u'ING. INDUSTRIALES', 'localidad': u'MORELIA', 'pais':   u'M\xe9xico', 'noInterior': 'N/A', 'colonia': u'BUENAVISTA 1A ETAPA', 'noExterior': u'215', 'codigoPostal': u'58228', 'estado': u'Michoac\xe1n', 'municipio': u'MORELIA'}, 
       'cfdi:DomicilioFiscal': {'calle': u'ING. INDUSTRIALES', 'localidad': u'MORELIA', 'pais': u'M\xe9xico', 'noInterior': 'N/A', 'colonia': u'BUENAVISTA 1A ETAPA', 'noExterior': u'215', 'codigoPostal': u'58228', 'estado': u'Michoac\xe1n', 'municipio': u'MORELIA'}, 
        }}

The structure consists of dictionaries inside a dictionary. I need to convert it to XML, then I got the following code to achieve that:

def dict2xml(data_dict, node=False, doc=False):

    parent = False
    if node:
        parent = True

    for element, attribute in data_dict.iteritems():
        if not parent:
            doc = minidom.Document()
        if isinstance(attribute, dict):
            if not parent:
                node = doc.createElement(element)
                dict2xml(attribute, node, doc)
            else:
                child = doc.createElement(element)
                dict2xml(attribute, child, doc)
                node.appendChild(child)
        elif isinstance(attribute, list):
            child = doc.createElement(element)
            for attr in attribute:
                if isinstance(attr, dict):
                    dict2xml(attr, child, doc)
            node.appendChild(child)
        else:
            if isinstance(attribute, str) or isinstance(attribute, unicode):
                attribute = str(attribute)
            else:
                    attribute = str(attribute)
            node.setAttribute(element, attribute)
            # print "attribute",unicode( attribute, 'UTF-8')
    if not parent:
        doc.appendChild(node)
    print doc.toprettyxml(indent="     ", encoding='utf-8')
    return doc

This function works just fine with the previous dictionary and returns this:

<cfdi:Emisor nombre="ALFATECH, S.A. DE C.V." rfc="ALF040329CX6">
    <cfdi:RegimenFiscal Regimen="Personas morales del régimen general"/>
    <cfdi:ExpedidoEn calle="ING. INDUSTRIALES" codigoPostal="58228" colonia="BUENAVISTA 1A ETAPA" estado="Michoacán" localidad="MORELIA" municipio="MORELIA" noExterior="215" noInterior="N/A" pais="México"/>
    <cfdi:DomicilioFiscal calle="ING. INDUSTRIALES" codigoPostal="58228" colonia="BUENAVISTA 1A ETAPA" estado="Michoacán" localidad="MORELIA" municipio="MORELIA" noExterior="215" noInterior="N/A" pais="México"/>
</cfdi:Emisor>

which is what I was waiting, but now I need to do the same with this dictionary that has basically the same structure but contains different elements:

d3={ 'catalogocuentas:Catalogo': {'rfc': u'ALF040329CX6', 'xmlns:catalogocuentas':'"http://www.sat.gob.mx/catalogocuentas"', 'xmlns:xsi':'"http://www.w3.org/2001/XMLSchema-instance"', 'xsi:schemaLocation':'"http://www.sat.gob.mx//catalogocuentas"', 'Ano':'2014', 'Mes':'02', 'TotalCtas':'219','version':'1.0', 
     'catalogocuentas:Ctas': {'Natur': u'D', 'nivel':'2', 'SubCtaDe':'1110-000-000', 'Desc':'CAJA CHICA', 'NumCta':'1110-001-000', 'CodAgrup':'1.1'}, 
      'catalogocuentas:Ctas': {'Natur': u'D', 'nivel':'3', 'SubCtaDe':'1120-001-000', 'Desc':'Banamex 741107-1', 'NumCta':'1120-001-001', 'CodAgrup':'2.1'}, 
       'catalogocuentas:Ctas': {'Natur': u'D', 'nivel':'3', 'SubCtaDe':'1120-001-000', 'Desc':'Bancomer 12911256971', 'NumCta':'1120-001-002', 'CodAgrup':'2.1'} 
        }}

You guys can see that the main difference is that this dictionary has THE SAME TAG for the inner elements(dictionaries), and I got the following result:

<?xml version="1.0" encoding="utf-8"?>
   <catalogocuentas:Catalogo Ano="2014" Mes="02" TotalCtas="219" rfc="ALF040329CX6"     version="1.0" xmlns:catalogocuentas="&quot;http://www.sat.gob.mx/catalogocuentas&quot;" xmlns:xsi="&quot;http://www.w3.org/2001/XMLSchema-instance&quot;" xsi:schemaLocation="&quot;http://www.sat.gob.mx//catalogocuentas&quot;">
         <catalogocuentas:Ctas CodAgrup="2.1" Desc="Bancomer 12911256971" Natur="D" NumCta="1120-001-002" SubCtaDe="1120-001-000" nivel="3"/>
   </catalogocuentas:Catalogo>

As you can see I only got THE LAST element(dictionary) from d3, and not all of them.

From this I can conclude that the problem might be that in this dictionary I have the same tag for all my elements.

Is this the real problem?

Any suggestions will be highly appreciated!!!

Thanks in advance.

Diego Calzadilla
  • 309
  • 6
  • 19

2 Answers2

1

Your code formatting makes my eyes hurt. The Python formatting style guide is your friend. In particular you abuse the maximum line length.

If you want to format python dictionaries into XML, there are a number of standard options. See this previous question for some solutions.

Python dictionary only allow a single element based on the same key, duplicate entries overwrite previous entries.

Community
  • 1
  • 1
Gary Walker
  • 8,831
  • 3
  • 19
  • 41
  • Thanks, so ... if python overwrite previous entries... it is definetely impossible to do it using minidom?? – Diego Calzadilla Aug 12 '14 at 04:08
  • You cannot construct a Python dictionary having duplicate keys. It is not a question of DOM or XML. You are not constructing the dictionary you thought you where. You can construct a Python dict where a single key value is a list of objects and serialize that to XML. – Gary Walker Aug 12 '14 at 04:10
0

There are libraries that already exist for this, one of which I created. Using the xmler package you can take your dictionary and turn it in to the following xml pretty easily:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" ?>
<cfdi:Emisor xmlns:cfdi="www.sat.gob.mx/cfd/3">
    <cfdi:DomicilioFiscal>
        <pais>Mexico</pais>
        <localidad>MORELIA</localidad>
        <estado>Michoacán</estado>
        <noExterior>215</noExterior>
        <municipio>MORELIA</municipio>
        <calle>ING. INDUSTRIALES</calle>
        <noInterior>N/A</noInterior>
        <codigoPostal>58228</codigoPostal>
        <colonia>BUENAVISTA 1A ETAPA</colonia>
    </cfdi:DomicilioFiscal>
    <nombre>ALFATECH, S.A. DE C.V.</nombre>
    <cfdi:ExpedidoEn>
        <pais>Mexico</pais>
        <localidad>MORELIA</localidad>
        <estado>Michoacán</estado>
        <noExterior>215</noExterior>
        <municipio>MORELIA</municipio>
        <calle>ING. INDUSTRIALES</calle>
        <noInterior>N/A</noInterior>
        <codigoPostal>58228</codigoPostal>
        <colonia>BUENAVISTA 1A ETAPA</colonia>
    </cfdi:ExpedidoEn>
    <rfc>ALF040329CX6</rfc>
    <cfdi:RegimenFiscal>
        <Regimen>Personas morales del régimen general</Regimen>
    </cfdi:RegimenFiscal>
</cfdi:Emisor>

To get that XML structure and to get it to pretty print correctly you'll have to modify your dictionary a little bit as you currently aren't defining the xml namespaces for cfdi. I used the following dictionary:

d = {
    "cfdi:Emisor": {
        "@attrs": {
            "xmlns:cfdi": "www.sat.gob.mx/cfd/3"
        },
        "rfc": "ALF040329CX6",
        "nombre": "ALFATECH, S.A. DE C.V.",
        "cfdi:RegimenFiscal": {
            "Regimen": "Personas morales del r\xe9gimen general"
        },
        "cfdi:ExpedidoEn": {
            "calle": "ING. INDUSTRIALES",
            "localidad": "MORELIA",
            "pais": "Mexico",
            "noInterior": "N/A",
            "colonia": "BUENAVISTA 1A ETAPA",
            "noExterior": "215",
            "codigoPostal": "58228",
            "estado": "Michoac\xe1n",
            "municipio": "MORELIA"
        },
        "cfdi:DomicilioFiscal": {
            "calle": "ING. INDUSTRIALES",
            "localidad": "MORELIA",
            "pais": "Mexico",
            "noInterior": "N/A",
            "colonia": "BUENAVISTA 1A ETAPA",
            "noExterior": "215",
            "codigoPostal": "58228",
            "estado": "Michoac\xe1n",
            "municipio": "MORELIA"
        }
    }
}

and then ran

print(xmler(d, customRoot=None, pretty=True))

Notice the @attrs key in the dictionary. It adds attributes to an element. You could also use the @ns key here to set the namespaces rather than using a colon separated string.

watzon
  • 2,401
  • 2
  • 33
  • 65