在Python开发中,我们经常会遇到上游系统以XML格式推送数据,而业务逻辑更习惯用字典来取值和序列化。把XML转成字典并不是简单把尖括号去掉,而是要保留层级、属性以及文本内容。下面介绍几种常见且稳定的实现方式。

一、使用标准库xml.etree.ElementTree手动递归
Python内置的xml.etree.ElementTree模块可以解析XML字符串或文件,将每个节点表示为Element对象。通过递归遍历子节点,我们能把树形结构映射为嵌套字典。这种方式不依赖第三方包,适合对运行环境有严格限制的项目。
下面的示例展示了一个基础递归函数,它把节点的标签作为键,子节点递归为值,并将节点文本存入特定键。注意,如果多个子节点标签相同,简单写法会覆盖前面的内容,实际项目中常改为列表存储。
import xml.etree.ElementTree as ET
def elem_to_dict(elem):
result = {}
# 处理当前节点的属性
if elem.attrib:
result['@attributes'] = dict(elem.attrib)
# 递归处理子节点
for child in elem:
child_data = elem_to_dict(child)
if child.tag in result:
# 如果已存在相同标签,转为列表
if not isinstance(result[child.tag], list):
result[child.tag] = [result[child.tag]]
result[child.tag].append(child_data)
else:
result[child.tag] = child_data
# 处理文本
text = (elem.text or '').strip()
if text:
result['#text'] = text
return result
xml_str = '<user id="1"><name>张三</name><age>28</age></user>'
root = ET.fromstring(xml_str)
print(elem_to_dict(root))
这种写法的优势是逻辑完全可控,你可以决定属性放在哪个键、文本如何命名、列表如何合并。缺点是代码量较大,且对命名空间、CDATA等细节需要额外处理。当XML结构复杂时,维护成本会明显上升。
另外,ElementTree在解析不可信XML时存在 billion laughs 攻击风险,若数据来自外部,应结合defusedxml库来防御实体扩展攻击,避免服务资源被耗尽。
二、使用xmltodict第三方库
xmltodict是一个轻量库,它的目标就是让XML像JSON一样好用。它利用Expat底层解析器,把XML直接转成有序字典,并且默认将属性前缀为@,文本键为#text,与多数前端习惯一致。
安装后只需一行parse调用即可完成转换。以下代码演示了基本用法及如何处理重复标签自动变为列表的行为。
import xmltodict
xml_str = '<root><item>a</item><item>b</item><meta lang="cn">测试</meta></root>'
data = xmltodict.parse(xml_str)
print(data)
# 输出: OrderedDict([('root', OrderedDict([('item', ['a', 'b']), ('meta', OrderedDict([('@lang', 'cn'), ('#text', '测试')]))]))])
xmltodict还支持postprocessor参数,让你自定义键名转换规则,比如把驼峰标签转成下划线风格。对于需要反向操作的场景,unparse方法也能把字典还原为XML,在接口 Mock 或配置生成时非常方便。
该库依赖较少,性能在中等体量文件下表现良好。但若XML体积达到几十MB以上,由于生成嵌套字典开销,内存占用会比流式解析高,此时应评估是否改用迭代方式处理。
三、基于dict与xpath结合的精简方案
如果只关心某几个字段,不必全量转换。配合lxml库的xpath能力,可以精准提取后组装字典,既节省内存也提升可读性。
下面示例用lxml提取用户姓名与年龄,直接构造字典,避免整树转换。
from lxml import etree
xml_str = '<user><name>李四</name><age>30</age></user>'
tree = etree.fromstring(xml_str.encode('utf-8'))
data = {
'name': tree.xpath('//name/text()')[0],
'age': int(tree.xpath('//age/text()')[0])
}
print(data)
这种方式适合字段固定、结构稳定的接口。当标签路径变化频繁时,硬编码xpath会增加耦合,此时全量转字典反而更灵活。
综合来看,小工具或脚本推荐xmltodict,追求零依赖选ElementTree递归,定点提取则用xpath。理解每种方案的底层数据结构,才能避免键值丢失与类型错误。
四、常见误区与注意事项
不少人在转换后会直接用中括号取深层值,却忽略某些节点可能因无文本而变成空字典。建议封装一个安全取值函数,遇字典缺失返回默认值。
另外,XML标签名含有横线或数字开头时,转成字典后作为键是合法的,但若后续用Python点号访问就会报错,应保持用中括号语法。处理命名空间时,xmltodict会把命名空间带入键名,可通过namespaces参数剥离,减少干扰。
import xmltodict xml_with_ns = '<ns:root xmlns:ns="http://ipipp.com/ns"><ns:val>1</ns:val></ns:root>' clean = xmltodict.parse(xml_with_ns, process_namespaces=True) print(clean)
只要提前规划好键名规范与异常分支,XML转字典就能成为你处理异构数据的高效入口,而不是埋坑源头。
PythonXML_to_dictXML解析修改时间:2026-08-02 06:54:27