Go的encoding/xml包不提供直接写文件函数,需组合序列化与I/O:中小结构体用xml.Marshal转[]byte后os.WriteFile;大结构体应改用xml.NewEncoder流式写入防内存溢出。

Go 的 encoding/xml 包本身不提供“直接写文件”函数,必须手动组合序列化与 I/O;用 xml.Marshal + os.WriteFile 最简,但大结构体易内存溢出,此时该换 xml.Encoder 流式写入。
用 xml.Marshal 生成字节再写入文件
这是最直观的做法:先转成 []byte,再落地。适合中小结构体(
- 结构体字段必须首字母大写(导出),否则
xml.Marshal完全忽略 - 每个字段需加
xml:标签,比如Name string `xml:"name"`,否则默认按字段名小写输出(name),且无法控制属性、省略等行为 - 记得手动拼接 XML 声明:
xml.Header是常量"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",不带它仍是合法 XML,但部分解析器会报 warning - 若要缩进可读,改用
xml.MarshalIndent(v, "", " "),第二个参数是每行前缀(通常空),第三个是每级缩进(如两个空格)
示例:
data, err := xml.MarshalIndent(person, "", " ")
if err != nil {
return err
}
output := []byte(xml.Header + string(data))
return os.WriteFile("person.xml", output, 0644)
用 xml.Encoder 流式写入避免内存峰值
当结构体嵌套深、含大切片(如上万条 []Item),xml.Marshal 会一次性把整个 XML 加载进内存,可能 OOM。此时应绕过字节缓冲,直接写入 io.Writer(如文件句柄)。
立即学习“go语言免费学习笔记(深入)”;
- 创建文件后传给
xml.NewEncoder,它返回一个可复用的编码器实例 - 调用
Encode方法一次写入整个结构体,内部自动分块、处理 token,内存占用恒定 - 若需 XML 声明,得手动调用
encoder.EncodeToken写入xml.ProcInst,不能依赖xml.Header -
Encoder不自动缩进,如需格式化,得自己实现Indent逻辑(较重),或接受紧凑输出
示例:
f, _ := os.Create("big.xml")
defer f.Close()
enc := xml.NewEncoder(f)
enc.EncodeToken(xml.ProcInst{Target: "xml", Inst: []byte(`version="1.0" encoding="UTF-8"`)})
enc.Encode(person) // 一次写入,无中间 []byte
字段值需特殊处理时(如 time.Time、Base64)必须实现 MarshalXML
encoding/xml 对基础类型有硬性限制:time.Time、自定义类型、含非法 UTF-8 字符的字符串等,默认会 panic 或输出空/错乱内容。不能靠标签解决,必须实现接口。
- 接收者用值类型(
func (v MyType) MarshalXML(...)),指针接收者在 encoder 内部解引用时可能 panic - 方法内应构造临时导出结构体(首字母大写字段),把原始值转换为字符串或兼容类型后再交由
e.EncodeElement编码 - 不要在
MarshalXML中直接调用xml.Marshal原始结构体,容易无限递归 - 常见踩坑:对
time.Time字段只做Format,却忘了设时区(UTC或本地);对 Base64 字段未处理空字符串或 nil 情况
嵌套、切片、属性这些标签细节决定 XML 是否合法
XML 结构是否符合协议要求,几乎全靠 struct tag 控制。写错一个逗号或空格,结果就可能错位或被忽略。
-
xml:"id,attr"表示作为当前元素的属性,不是子元素;漏掉,attr就变成<user><id>123</id></user> - 切片字段必须显式指定标签名,如
Items []Item `xml:"item"`,否则默认输出为<items><item>...</item></items>(多一层容器) - 想扁平化嵌套字段(跳过中间容器标签),用
xml:",inline",但仅对嵌入结构体有效,且不能和omitempty共存 -
xml:",chardata"和xml:",innerxml"容易混淆:chardata是纯文本节点(如<tag>text</tag>),innerxml是原样插入子 XML 片段(如<tag><b>bold</b></tag>),后者不校验合法性
真正麻烦的从来不是语法,而是某层嵌套里一个字段少写了 omitempty,导致空值字段污染了下游系统;或者某个 time.Time 字段没实现 MarshalXML,上线后 XML 解析失败才暴露——这些点往往在集成测试阶段才浮出水面。


















