Python xmltodict unparse 将字典转换为XML字符串

3次阅读

xmltodict.unparse() 返回空字符串或报错的根本原因是输入数据不符合xml根结构要求:顶层必须是单个键值对(如{“root”: {…}}),不能是列表、多键字典、none或无根扁平结构;需手动包裹根节点,且属性须用@前缀、文本用#text标识。

Python xmltodict unparse 将字典转换为XML字符串

xmltodict.unparse() 为什么返回空字符串或报错 TypeError: unhashable type: 'dict'

根本原因不是字典结构不对,而是传给 unparse() 的顶层数据不是合法的“XML根结构”:它要求顶层必须是单个键值对(即一个根元素),不能是列表、纯字符串、None,也不能是多个并列 key 的 dict。常见错误是直接把从 json 解析来的扁平 dict 或嵌套 list 丢进去。

  • ✅ 正确输入:{"root": {"name": "Alice", "age": "30"}} —— 单个根 key "root"
  • ❌ 错误输入:[{"item": "a"}, {"item": "b"}](list)、{"a": 1, "b": 2}(多根)、{"root": [...]} 但内部 list 没用 @attrs#text 约定好语义
  • ⚠️ 注意:unparse() 不会自动帮你加根;如果原始数据没根,得先包一层,比如 {"data": your_dict}

怎么处理含属性、文本混合的节点(比如 <tag id="123">text</tag>

xmltodict 对属性和文本的约定是硬编码的:@ 开头的 key 表示属性,#text 表示标签内纯文本。不按这个规则写,生成的 XML 就会漏属性或把文本当子元素。

  • 要生成 <person id="100" type="user">John</person>,字典得写成:
    {"person": {"@id": "100", "@type": "user", "#text": "John"}}
  • 如果同时有子元素和文本(XML 不推荐但允许),#text 和其它 key 可共存,但需确保解析器能接受这种混合
  • 属性名不能含空格或特殊字符;@ 是固定前缀,不能换成 $ 或省略

中文、特殊字符、CDATA 怎么安全输出

unparse() 默认用 UTF-8 编码,但不会自动转义或包裹 CDATA。如果你的文本含 、<code>&、中文,只要输入是 python str(非 bytes),它就能正常编码输出 —— 前提是调用时显式指定 encodingfull_document

  • 中文没问题:unparse({"root": "你好"}, encoding="utf-8") → 正确输出带 <?xml version="1.0" encoding="utf-8"?> 的字符串
  • 想禁用 XML 声明?传 full_document=False;否则默认为 True,开头必带声明行
  • 需要 CDATA?xmltodict 本身不支持;得手动替换,比如生成后用 .replace(" 预留占位,再正则替换内容 —— 这属于业务层补救,不是 <code>unparse() 职责

性能差、内存暴涨?小心深层嵌套和大字符串

unparse() 是纯 Python 实现,递归构建字符串。当字典嵌套超 100 层,或某个 #text 值是几 MB 的 base64 字符串时,它会明显变慢,甚至触发 RecursionError 或吃光内存。

立即学习Python免费学习笔记(深入)”;

  • 深度嵌套:Python 默认递归限制约 1000 层,可用 sys.setrecursionlimit(2000) 临时放宽,但治标不治本;建议先 flatten 或分段生成
  • 大文本:避免把整个文件内容塞进 #text;改用外部引用或流式写入(这时就不该用 unparse(),该换 xml.etree.ElementTree
  • 对比实测:10 万行简单记录用 unparse() 生成耗时约 1.2s;同数据用 ElementTree 构建再 tostring() 约 0.3s —— 量大时别图省事

真正难的从来不是“怎么调用”,而是判断该不该用 xmltodict.unparse():它适合小到中等、结构清晰、属性/文本规则明确的场景;一旦涉及流、校验、命名空间或性能敏感,就该切回原生 XML 库。

text=ZqhQzanResources