xmltodict.parse() 默认返回 ordereddict 以兼容旧版本并明确保留顺序,但会导致与只接受普通 dict 的函数(如旧版 json.dumps)不兼容;可通过 dict_constructor=dict 强制使用 dict,但需注意同名节点覆盖问题,应配合 force_list 指定重复标签转列表,同时命名空间需启用 process_namespaces 并设置 namespace_separator 避免 key 混乱,大文件应避免 parse() 而改用流式解析。

xmltodict.parse() 为什么返回 OrderedDict 而不是 dict
因为 xmltodict.parse() 默认保留 XML 元素顺序,而 Python 3.6+ 的 dict 虽然也保持插入顺序,但库为兼容旧版本和明确语义,仍默认返回 OrderedDict。这在你用 == 比较结果、或传给某些只认原生 dict 的函数(比如 json.dumps() 在老版本中)时会出问题。
- 加参数
dict_constructor=dict强制用普通dict:xmltodict.parse(xml_str, dict_constructor=dict) - 如果只是想让
json.dumps()不报错,更轻量的做法是:直接传给它——现代json模块已支持OrderedDict - 注意:一旦用了
dict_constructor=dict,同名兄弟节点(多个相同标签)会被后一个覆盖,除非你同时启用force_list
多个同名子节点被合并成单个 dict,而不是 list
这是最常踩的坑:XML 里连续两个 <item>...</item>,默认解析出来是最后一个的值,前面的“消失”了。因为 xmltodict 把重复标签当成了键冲突,直接覆盖。
- 用
force_list参数指定哪些标签必须转成列表:xmltodict.parse(xml, force_list=['item', 'entry']) - 也可以全局强制所有重复标签都变 list:
force_list=True,但会多出很多不必要的[...],不推荐 - 如果只对某一层生效,比如只想让
root.items.item是 list,而root.meta.version还是 str,那就精确写标签名,别偷懒用通配
带命名空间的 XML 解析后 key 名混乱
像 <rss xmlns:dc="http://purl.org/dc/elements/1.1/"></rss> 这种,解析后字段可能变成 @xmlns:dc 或嵌套进 #text,根本没法直读。
- 先用
process_namespaces=True开启命名空间处理:xmltodict.parse(xml, process_namespaces=True) - 再通过
namespace_separator定义分隔符,默认是:,但容易和属性名冲突;建议改成|:namespace_separator='|' - 这样
<creator>Alice</creator>就变成{'dc|creator': 'Alice'},清晰可读,也方便用.get()安全取值 - 注意:开启
process_namespaces后,原本的@前缀属性(如<tag id="123"></tag>→@id)不受影响,两者共存
大文件解析内存爆掉或卡死
xmltodict.parse() 是一次性加载整个 XML 字符串进内存再解析,遇到几十 MB 的文件,Python 进程很可能被系统 kill,或者卡住几秒甚至几分钟。
立即学习“Python免费学习笔记(深入)”;
- 不要用
parse()处理大文件,改用xmltodict.parse()的流式替代方案:配合xml.sax或lxml.etree.iterparse()手动边读边转,xmltodict本身不提供流式 API - 如果 XML 结构简单(比如全是平铺记录),考虑用正则粗筛 +
xml.etree.ElementTree分块解析,比全量转字典省 90% 内存 - 真要硬上
xmltodict,至少先用gzip.open()或requests.get(..., stream=True)控制原始数据流,别把压缩包或网络响应体全读进内存再解压再 parse
命名空间处理和 force_list 的组合逻辑容易互相干扰,调的时候建议先固定一个变量,再加另一个,不然连哪行配置导致 key 变成 None 都找不到。










