You can pass a postprocessing function to xmltodict, which allows you to modify the dict values.
But postprocecssing needs to return a key and a value:

import xmltodict
import json


def postprocessor(path, key, value):
    if key == 'Pokemons' and not value:
        return key, []
    return key, value

xml = """<Something>
    <Guitar>
        <name>Walden</name>
        <strings>5</strings>
    </Guitar>
    <Pokemons>
    </Pokemons>
</Something>"""


res = xmltodict.parse(xml, postprocessor=postprocessor)
print(json.dumps(res, indent=2, sort_keys=True))

Output:

{
  "Something": {
    "Guitar": {
      "name": "Walden",
      "strings": "5"
    },
    "Pokemons": []
  }
}
Answer from Maurice Meyer on Stack Overflow
🌐
GitHub
github.com › martinblech › xmltodict › blob › master › xmltodict.py
xmltodict/xmltodict.py at master · martinblech/xmltodict
>>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value · >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>',  ...
Author   martinblech
🌐
PyPI
pypi.org › project › xmltodict
xmltodict · PyPI
postprocessor=None: Function to modify parsed items.
      » pip install xmltodict
    
Published   Feb 22, 2026
Version   1.0.4
🌐
Omz Software
omz-software.com › pythonista › docs › ios › xmltodict.html
xmltodict — Python 3.6.1 documentation
February 19, 2020 - >>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>', ... postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of expat (such as defusedexpat) by using the expat parameter.
🌐
Readthedocs
xmltodict.readthedocs.io › en › latest › CHANGELOG
CHANGELOG - xmltodict
Merge branch 'master' of github.com:martinblech/xmltodict · preserve xml attribute order (fixes #13) fix #12: postprocess cdata items too · added info about official fedora package · Merge pull request #11 from ralphbean/master · Include REAMDE, LICENSE, and tests in the distributed tarball. take all characters (no need to strip and filter) fixed CLI (marshal only takes dict, not OrderedDict) ignore MANIFEST · implemented postprocessor callback (#6) update readme with install instructions ·
🌐
Tanium
tanium.github.io › pytan › _modules › xmltodict.html
xmltodict — PyTan v2.1.6 2.1.6 documentation
postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of `expat` (such as `defusedexpat`) by using the `expat` parameter. E.g: >>> import defusedexpat >>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat) OrderedDict([(u'a', u'hello')]) """ handler = _DictSAXHandler(namespace_separator=namespace_separator, **kwargs) if isinstance(xml_input, _unicode): if not encoding: encoding = 'utf-8' xml_input = xml_input.encode(encoding) if not process_namespaces: namespace_separator = None parser = expat.ParserCreate
🌐
Stack Overflow
stackoverflow.com › questions › 53744074 › xmltodict-unparse-parse-not-the-same
python - xmltodict unparse parse not the same - Stack Overflow
def postprocessor(path, key, value): if key.startswith("value"): try: return key, int(value) except (ValueError, TypeError): return key, value return key, value test_data_re = xmltodict.parse(xml_str, dict_constructor=dict, postprocessor=postprocessor) print("xml_str back to dict : ", test_data_re['settings']) This would produce: xml_str back to dict : {'value1': 1, 'parent_lvl1': {'parent_Lvl2': {'value1': 2, 'value2': 3}}} Btw, a good place to look for xmltodict sample usages is xmltodict tests, check it out.
🌐
GitHub
github.com › martinblech › xmltodict › issues › 107
postprocessor doesn't have effect on attributes? · Issue #107 · martinblech/xmltodict
June 11, 2015 - >>> def postprocessor(path, key, value): ... try: ... return key, int(value) ... except (ValueError, TypeError): ... return key, value >>> doc="""<mydoc has="an attribute" version=2><and><many>elements</many><many>more elements</many><numbers>2</numbers></and></mydoc>""" >>> json.dumps(xmltodict.parse(doc)) '{"mydoc": {"@has": "an attribute", "@version": "2", "and": {"many": ["elements", "more elements"], "numbers": "2"}}}' >>> json.dumps(xmltodict.parse(doc, postprocessor=postprocessor)) '{"mydoc": {"@has": "an attribute", "@version": "2", "and": {"many": ["elements", "more elements"], "numbers": 2}}}' As of here, @version should be converted integer but didn't, while numbers converted as integer 2.
Author   1kko
Find elsewhere
🌐
Readthedocs
warriorframework.readthedocs.io › en › latest › _modules › Framework › OSS › xmltodict.html
Framework.OSS.xmltodict — warriorframework documentation
postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of `expat` (such as `defusedexpat`) by using the `expat` parameter. E.g: >>> import defusedexpat >>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat) OrderedDict([(u'a', u'hello')]) You can use the force_list argument to force lists to be created even when there is only a single child of a given level of hierarchy.
🌐
Readthedocs
taf-docs.readthedocs.io › en › latest › _modules › taf › testlib › xmltodict.html
taf.testlib.xmltodict — taf 0.0.1 documentation
postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of `expat` (such as `defusedexpat`) by using the `expat` parameter. E.g: >>> import defusedexpat >>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat) OrderedDict([(u'a', u'hello')]) """ handler = _DictSAXHandler(*args, **kwargs) parser = expat.ParserCreate() parser.ordered_attributes = True parser.StartElementHandler = handler.startElement parser.EndElementHandler = handler.endElement parser.CharacterDataHandler = handler.characters try: parser.ParseF
🌐
GitHub
github.com › martinblech › xmltodict › blob › master › tests › test_xmltodict.py
xmltodict/tests/test_xmltodict.py at master · martinblech/xmltodict
def test_postprocessor(): def postprocessor(path, key, value): try: return key + ':int', int(value) except (ValueError, TypeError): return key, value · assert {'a': {'b:int': [1, 2], 'b': 'x'}} == parse('<a><b>1</b><b>2</b><b>x</b></a>', postprocessor=postprocessor) ·
Author   martinblech
🌐
GitHub
github.com › DimitriPapadopoulos › xmltodict › blob › 7e85c54011a1853c7588cb03d0cbb8cf12f08ef4 › xmltodict.py
xmltodict/xmltodict.py at 7e85c54011a1853c7588cb03d0cbb8cf12f08ef4 · DimitriPapadopoulos/xmltodict
>>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value · >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>',  ...
Author   DimitriPapadopoulos
🌐
HotExamples
python.hotexamples.com › examples › xmltodict › - › parse › python-parse-function-examples.html
Python parse Examples, xmltodict.parse Python Examples - HotExamples
March 15, 2019 - """ import xmltodict if self.postprocessor: obj = xmltodict.parse(f, attr_prefix=self.attr_prefix, postprocessor=self.postprocessor) else: obj = xmltodict.parse(f, attr_prefix=self.attr_prefix) # If node list was given, walk down the tree if self.node_list: for node in self.node_list: obj = obj[node] # If the top-level XML object in the file is a list # then yield each element separately; otherwise, yield # the top-level object.
🌐
PyPI
pypi.org › project › xmltodict-fast
xmltodict-fast · PyPI
xml = "<a><item>one</item></a>" xmltodict.parse(xml, force_list=("item",)) # {'a': {'item': ['one']}} ← always a list, even for a single element · def int_postprocessor(path, key, value): try: return key, int(value) except (ValueError, TypeError): return key, value xmltodict.parse("<root><count>42</count></root>", postprocessor=int_postprocessor) # {'root': {'count': 42}} mydict = {"line": {"points": [[1, 5], [2, 6]]}} print(xmltodict.unparse(mydict, pretty=True, expand_iter="coord")) <?xml version="1.0" encoding="utf-8"?> <line> <points> <coord>1</coord> <coord>5</coord> </points> <points> <coord>2</coord> <coord>6</coord> </points> </line> disable_entities=True (default) blocks XML entity expansion (billion-laughs / XML-bomb attacks).
      » pip install xmltodict-fast
    
Published   Mar 26, 2026
Version   1.0.0
🌐
OMZ Software
omz-software.com › editorial › docs › ios › xmltodict.html
xmltodict — Editorial Documentation
>>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>', ... postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of expat (such as defusedexpat) by using the expat parameter.
🌐
Unixunion
unixunion.github.io › libsolace.xml2dict.html
libsolace.xml2dict module — libsolace 2.0 documentation
>>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>', ...
🌐
OMZ Software
omz-software.com › pythonista2 › docs › ios › xmltodict.html
xmltodict — Pythonista Documentation
>>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>', ... postprocessor=postprocessor) OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) You can pass an alternate version of expat (such as defusedexpat) by using the expat parameter.
🌐
GitHub
github.com › martinblech › xmltodict › blob › master › CHANGELOG.md
xmltodict/CHANGELOG.md at master · martinblech/xmltodict
Merge branch 'master' of github.com:martinblech/xmltodict · preserve xml attribute order (fixes #13) fix #12: postprocess cdata items too · added info about official fedora package · Merge pull request #11 from ralphbean/master · Include README, LICENSE, and tests in the distributed tarball. take all characters (no need to strip and filter) fixed CLI (marshal only takes dict, not OrderedDict) ignore MANIFEST · #8 preprocessing callback in unparse() implemented postprocessor callback (#6) update readme with install instructions ·
Author   martinblech
🌐
Unixunion
unixunion.github.io › _modules › libsolace › xml2dict.html
libsolace.xml2dict — libsolace 2.0 documentation
Usage example:: >>> def postprocessor(path, key, value): ... try: ... return key + ':int', int(value) ... except (ValueError, TypeError): ... return key, value >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>', ...
🌐
ProgramCreek
programcreek.com › python › example › 82408 › xmltodict.parse
Python Examples of xmltodict.parse
def _add_batch(self, catalog_entry, job_id, start_date, order_by_clause=True): endpoint = "job/{}/batch".format(job_id) url = self.bulk_url.format(self.sf.instance_url, endpoint) body = self.sf._build_query_string(catalog_entry, start_date, order_by_clause=order_by_clause) headers = self._get_bulk_headers() headers['Content-Type'] = 'text/csv' with metrics.http_request_timer("add_batch") as timer: timer.tags['sobject'] = catalog_entry['stream'] resp = self.sf._make_request('POST', url, headers=headers, body=body) batch = xmltodict.parse(resp.text) return batch['batchInfo']['id']