XML解析首选xml.etree.ElementTree,轻量标准无依赖;命名空间需预定义字典,取值用itertext()防空;XML须转dict或Context供docx-template渲染,复杂结构推荐xmltodict;重复节点强制转list并补默认值;模板加载须二进制模式,中文指定UTF-8编码。

XML解析用xml.etree.ElementTree最稳,别碰minidom或第三方XML库
Python原生xml.etree.ElementTree足够处理绝大多数结构化XML,轻量、标准、无额外依赖。用minidom容易在命名空间、编码、空白文本上翻车;用lxml虽强但引入C依赖,CI环境常出问题。
- 直接
import xml.etree.ElementTree as ET,ET.parse("file.xml")加载,.getroot()拿到根节点 - 遇到带命名空间的XML(比如
{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p),先定义ns = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"},再用root.findall(".//w:p", ns) - 别用
.text直接取值——空元素、换行、子标签混排时返回None或意外空白;改用"".join([t for t in elem.itertext()]).strip()
docx-template只认字典,XML数据得先扁平化成dict或Context对象
docx-template底层靠Jinja2渲染,模板里写{{ user.name }},它就真要你传一个带name属性的user对象,或者一个{"user": {"name": "张三"}}字典。XML是树状结构,不能直接喂进去。
- 简单XML:用递归函数把
Element转成嵌套dict,注意同名多节点要转成list(比如多个<item></item>) - 复杂XML:别手写转换,用
xmltodict.parse(xml_str)(需pip install xmltodict),它会把XML转成接近JSON结构的dict,再按模板字段路径取值 - 避免深层嵌套传参——模板里写
{{ data.section.list.0.title }}可读性差还易错;提前在Python里拆好,比如context = {"title": data["section"]["list"][0]["title"]}
模板里用{% for %}循环填表,但XML中重复节点必须显式转成list
XML里<row><cell>A</cell></row><row><cell>B</cell></row>这种结构,解析后默认是单个row对象(最后那个覆盖前面的),docx-template的{% for row in rows %}会报TypeError: 'NoneType' object is not iterable。
- 解析时强制转
list:rows = root.findall("row") if root.findall("row") else [],再用[{"cell": r.find("cell").text.strip()} for r in rows] - 模板里写
{% for row in rows %}<t>{{ row.cell }}</t>{% endfor %},注意docx-template生成的是Word XML片段,不是HTML,别漏掉<t></t>标签(如果用docxtpl的RichText等高级功能另说) - 空列表会导致整个
{% for %}块消失——如果“无数据”也要显示“暂无记录”,得在Python里补默认值:"rows": rows or [{"cell": "暂无记录"}]
中文乱码、样式丢失、图片不显示?检查docx-template初始化和render()参数
常见问题不是XML或模板本身,而是DocxTemplate实例化或渲染时漏了关键参数。
立即学习“Python免费学习笔记(深入)”;
- 加载模板必须用二进制模式:
DocxTemplate("template.docx"),别用open("template.docx", "r")——会破坏ZIP结构 - 中文必须指定
encoding="utf-8"(虽然默认是UTF-8,但某些Windows环境会fallback到GBK,导致{{ name }}渲染成乱码) - 含图片的模板,XML里
<drawing></drawing>部分不会被docx-template自动替换;图片得用inline_images参数传入bytes或PIL.Image对象,并在模板里用{% docx_replace_image "img1" %}标记 - 样式继承失效?确保原始
.docx模板里段落/表格样式已定义(比如“标题1”“正文”),docx-template不会创建新样式,只复用已有样式名
items被当成了字符串而不是列表。动手前,先用print(json.dumps(context, indent=2, ensure_ascii=False))看看你塞进去的到底是什么。











