1

I was writing my aplication using minidom but minidom does not preserve attribute order(sorts alphabetically), so I decided to do it using lxml.

However in the following lines of code I'm not getting the desired order:

import lxml.etree as ET
SATNS = "link_1"
NS = "link_2"
location_attribute = '{%s}schemaLocation' % NS
root = ET.Element('{%s}Catalogo' % SATNS, nsmap={'catalogocuentas':SATNS}, attrib=
   {location_attribute: 'http://www.sat.gob.mx/catalogocuentas'}, Ano="2014",       Mes="02",   TotalCtas="219", RFC="ALF040329CX6", Version="1.0")
print (ET.tostring(root, pretty_print=True))

This is what I'm expecting to get:

<catalogocuentas:Catalogo xmlns:catalogocuentas="link_1"
xmlns:xsi="link_2" xsi:schemaLocation="http://www.sat.gob.mx/catalogocuentas"
Ano="2014" Mes="02" TotalCtas="219" RFC="XXX010101XXX" Version="1.0">
</catalogocuentas:Catalogo>

Which is in the order that I filled in:

root=ET.element(...)

But I'm getting the next, that has no order:

<catalogocuentas:Catalogo xmlns:catalogocuentas="link_1" 
xmlns:xsi="link_2" RFC="ALF040329CX6" Version="1.0" 
Mes="02" xsi:schemaLocation="http://www.sat.gob.mx/catalogocuentas" Ano="2014" TotalCtas="219">
</catalogocuentas:Catalogo>

Is there a way to fix this problem?

Thanks in advance!!

Diego Calzadilla
  • 309
  • 6
  • 19
  • I'm pretty sure there is a way to give `lxml` a custom dictionary type to use for attribute dictionaries, so you could pass it `collections.OrderedDict`. However, as Patrick Collins's answer explains, this is almost definitely a bad idea. (IIRC, you do it by either subclassing the default parser class, or creating an instance of the default class and setting a member on it, then either way passing it as the `parser=` argument to the `Element` constructor, the `parse` function, or whatever else you start off with. – abarnert Aug 27 '14 at 03:41
  • That being said, if you want it to preserve order, you have to give it the attributes in order in the first place, and keyword arguments to a function are arbitrarily ordered. (See [PEP 468](http://legacy.python.org/dev/peps/pep-0468/) for an open proposal to change that, but it seems to be stalled, even though Guido and the core devs were relatively open to the idea after a bit of discussion…) – abarnert Aug 27 '14 at 03:49
  • possible duplicate of [python - lxml: enforcing an specific order for attributes](http://stackoverflow.com/questions/14917943/python-lxml-enforcing-an-specific-order-for-attributes) – mzjn Aug 27 '14 at 10:15

1 Answers1

3

Dictionaries in Python are unordered. Keyword arguments are passed to functions by a dictionary traditionally named **kwargs, and so the order is lost. The function can't possibly know what order the arguments to ET.element came in.

As stated in this question, there isn't really any way to get this done. XML doesn't care about attribute order, so there isn't really any good reason to do it.

Community
  • 1
  • 1
Patrick Collins
  • 10,306
  • 5
  • 30
  • 69
  • I totally agree with you, the question here is that I need to send my XML to a XML validator that doesn't accept my XML if it's not in the order defined that I'm trying to reach – Diego Calzadilla Aug 27 '14 at 04:39
  • @DiegoCalzadilla The XML validator has a (very serious) bug, you should file a report with whoever maintains it. – Patrick Collins Aug 27 '14 at 04:46
  • @DiegoCalzadilla From the third question I linked: "As far as I know, lxml has no mechanism for specifying the order attributes appear in serialized XML, and I would be surprised if it did." – Patrick Collins Aug 27 '14 at 04:51
  • @DiegoCalzadilla It looks like html5lib might have what you want: https://github.com/html5lib/html5lib-python/issues/37 – Patrick Collins Aug 27 '14 at 04:53