Serialize XML with stable attribute insertion order
Also published in our Blogger archive.
Serialize XML with stable attribute insertion order
When a consumer compares ordinary XML text, attribute order can matter to that consumer even though XML attribute order has no semantic meaning. In Python 3.8 and later, xml.etree.ElementTree.tostring() preserves the attribute order specified by the user. Build or set attributes in the desired order instead of sorting them accidentally through an unrelated transformation.
The example creates a <task> element, then calls set() for id, state, and priority in that sequence. tostring(..., encoding="unicode") returns text rather than bytes. The exact-string assertion makes the intended serializer result visible, and the printed line is deterministic for Python 3.8+.
This is a serialization convention, not XML canonicalization. It does not mean another XML producer will use the same order, nor does it make two semantically equivalent documents byte-identical when whitespace, namespace declarations, escaping, or other serializer choices differ. For byte-oriented comparisons or signature workflows, ElementTree documents canonicalize() as a C14N 2.0 transformation; it was added in Python 3.8 and has different rules, including attribute ordering. Keep the ordinary serializer when the objective is a readable, intentionally ordered local representation.
AI assistance disclosure: this article was drafted with AI assistance and should be checked against the receiving system’s XML requirements.
Source: Python ElementTree documentation.
import xml.etree.ElementTree as ET
task = ET.Element("task")
task.set("id", "T-17")
task.set("state", "queued")
task.set("priority", "high")
xml_text = ET.tostring(task, encoding="unicode")
expected = '<task id="T-17" state="queued" priority="high" />'
assert xml_text == expected
assert list(task.attrib) == ["id", "state", "priority"]
print(xml_text)
<task id="T-17" state="queued" priority="high" />