Skip to content

Instantly share code, notes, and snippets.

@sebastianknopf
Last active June 19, 2025 17:41
Show Gist options
  • Save sebastianknopf/69753b2f588ef3ca09df7b34fb70e65d to your computer and use it in GitHub Desktop.
Save sebastianknopf/69753b2f588ef3ca09df7b34fb70e65d to your computer and use it in GitHub Desktop.
utility module to convert a python dict to XML
from lxml import etree
from lxml.etree import CDATA
from lxml.etree import QName
_CONST_ATTRIB_INDICATOR = '#'
def _build_xml_element(parent, d, wrap_lists, nsmap):
if isinstance(d, dict):
attrib = {}
content = {}
for key, value in d.items():
if key == '$name' or key == '$text' or key == '$ns':
pass
elif key.startswith(_CONST_ATTRIB_INDICATOR):
attrib[key.replace(_CONST_ATTRIB_INDICATOR, '')] = str(value)
else:
content[key.replace(_CONST_ATTRIB_INDICATOR, '')] = value
parent.attrib.update(attrib)
if parent.text is None:
for key, value in content.items():
if isinstance(value, list):
if wrap_lists:
wrap = etree.SubElement(parent, key)
for item in value:
if not '$name' in item:
raise RuntimeError('wrapped list element does not contain required key $name')
name = item['$name']
if '$ns' in item and item['$ns'] in nsmap:
ns_url: str = nsmap[item['$ns']]
name = QName(f"{{{ns_url}}}{name}")
child = etree.SubElement(wrap, name)
_build_xml_element(child, item, wrap_lists, nsmap)
else:
for item in value:
name = item['$name'] if '$name' in item else key
if '$ns' in item and item['$ns'] in nsmap:
ns_url: str = nsmap[item['$ns']]
name = QName(f"{{{ns_url}}}{name}")
child = etree.SubElement(parent, name)
_build_xml_element(child, item, wrap_lists, nsmap)
else:
if isinstance(value, dict) and '$name' in value:
name = value['$name']
else:
name = key
if isinstance(value, dict) and '$ns' in value and value['$ns'] in nsmap:
ns_url: str = nsmap[value['$ns']]
name = QName(f"{{{ns_url}}}{name}")
child = etree.SubElement(parent, name)
if isinstance(value, dict) and '$text' in value:
text: str = value['$text']
if text.startswith('<![CDATA['):
text = text.replace('<![CDATA[', '').replace(']]>', '')
child.text = CDATA(text)
else:
child.text = str(text)
_build_xml_element(child, value, wrap_lists, nsmap)
else:
_build_xml_element(child, value, wrap_lists, nsmap)
else:
d = str(d)
if d.startswith('<![CDATA['):
d = d.replace('<![CDATA[', '').replace(']]>', '')
parent.text = CDATA(d)
else:
parent.text = str(d)
def dict2xml(root, d, pretty_print=True, xml_declaration=True, wrap_lists=False, nsmap: dict[str, str] = {}) -> str:
root = etree.Element(root, nsmap=nsmap)
_build_xml_element(root, d, wrap_lists, nsmap)
if pretty_print:
etree.indent(root, space=" ")
xml_str: str = etree.tostring(
root,
pretty_print=pretty_print,
xml_declaration=xml_declaration,
encoding='UTF-8'
)
return xml_str
import json
from dict2xml import dict2xml
dc: dict = {
'Person': {
'$name': 'DiversePerson',
'#version': '1.0',
'Name': 'Max Mustermann',
'Children': [
{
'$name': 'Child',
'#version': '1.0',
'Name': 'Julia Mustermann'
},
{
'$name': 'Child',
'#version': '1.0',
'Name': 'Lena Mustermann'
}
],
'SomeSubElement': {
'$name': 'Sub',
'$text': 'Value',
'$ns': 'myns',
'#type': 'named',
'Test': {
'Tag': 'Val'
}
},
'AnotherSub': {
'$text': '<![CDATA[<a href="vpe.de">VPE</a>]]>',
'$ns': 'anotherns'
},
'ElementWithInvalidNamespace': {
'$text': 'SomeRandomText',
'$ns': 'invalidns'
}
}
}
print('Initial Dict:')
print(json.dumps(dc, indent=4))
print()
namespaces: dict = {
'myns': 'http://my.name.space',
'anotherns': 'http://another.name.space'
}
print('Unwrapped List:')
print(dict2xml('People', dc, wrap_lists=False, nsmap=namespaces).decode('utf-8'))
print()
print('Wrapped List')
print(dict2xml('People', dc, wrap_lists=True, nsmap=namespaces).decode('utf-8'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment