BurntSushi/toml 是 Go 社区事实标准的 TOML 解析库,稳定轻量、反射友好,支持 DecodeFile/Decode 直接映射到 struct,但不支持序列化(无 Encode);写入需换用 pelletier/go-toml/v2 等双向库。

用 github.com/BurntSushi/toml 读配置最稳,但不支持写入
这是目前 Go 社区事实标准的 TOML 解析库,稳定、轻量、反射友好,DecodeFile 和 Decode 能直接把文件或字节流映射到 struct。但它**没有提供序列化(encode)能力**——想改完再写回去?得自己打开文件、调 NewEncoder,而且它不支持部分高级特性(如 inline table 写入、注释保留)。
常见错误现象:toml.DecodeFile("config.toml", &conf) 返回 nil 错误,但 conf 字段全是零值 → 很可能是 struct 字段名/标签没对齐 TOML key,或类型不匹配(比如 TOML 写了 port = "8080",但 struct 定义的是 Port int)。
- 字段名必须首字母大写(Go 可导出规则),否则反射看不见
- 嵌套表要用 struct 嵌套,例如
[database]对应Database DatabaseConfig字段 - 如果 TOML key 是小写或含下划线(如
connection_max),务必加toml:"connection_max"标签 -
time.Time能自动解析 ISO8601 时间字符串(如2025-03-29T14:22:00Z),但时区必须合法,否则 panic
写 TOML 必须换 github.com/pelletier/go-toml 或 go-toml/v2
BurntSushi/toml 不提供 Encode,硬写会报 undefined: toml.Encode。实际项目中要生成或更新配置,得切到支持双向操作的库。pelletier/go-toml v1 简单易上手;v2(即 github.com/pelletier/go-toml/v2)更现代,性能好、API 清晰,还支持注释、位置信息等,但 API 不兼容 v1。
使用场景:CI 自动生成配置、服务启动时初始化默认 config.toml、运行时热更新配置并落盘。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- v2 的
Marshal默认不缩进,加toml.MarshalWithOptions(conf, toml.MarshalOpt{Indent: " "})才好看 - 写 map 或 slice 时,v2 会按 key 字典序排序,和手动写的顺序可能不一致(不影响语义,但 diff 会跳)
- 如果要保留原始注释或格式(比如运维改过注释),v2 支持
ParseFS+Tree.Set+WriteTo,但复杂度陡增,一般没必要
DecodeFile 和 LoadFile 别混用,路径和错误处理逻辑不同
BurntSushi/toml 用 DecodeFile(path, &v),返回 (int, error),第一个返回值是成功解析的键数量;pelletier/go-toml v1 用 LoadFile(path),返回 (*Tree, error);v2 用 LoadFile(path) 返回 (*Document, error)。三者不能交叉调用,结构体字段标签也不通用。
容易踩的坑:LoadFile 成功后得到的是树形结构,要取值得用 tree.Get("database.host").(string),类型断言失败就 panic —— 不如 struct 绑定安全。
- 路径错误时,
DecodeFile报open config.toml: no such file or directory;LoadFile报类似错误但包名不同,别被误导 -
DecodeFile对空文件或全注释文件返回0, nil;LoadFile会报EOF或语法错误,行为不一致 - 若配置文件在子目录(如
./conf/app.toml),确保当前工作目录正确,或用filepath.Join拼绝对路径
嵌套数组、inline table、混合类型最容易解析失败
TOML 允许写 [[servers]](数组式表)、servers = [{ip="1.1.1.1"}, {ip="2.2.2.2"}](inline table)、甚至 ports = [80, "443", 8080](混合类型)。Go struct 无法表达混合数组,BurntSushi/toml 遇到就会 decode 失败,报 cannot unmarshal TOML array into int 这类错误。
真实案例:TOML 里写了 ports = [80, "443"],struct 定义 Ports []int → 直接 panic;改成 []interface{} 虽能过,但后续用还得手动类型断言。
- 数组统一用同类型,避免
[80, "443"]这种写法(TOML 合法,Go 解析痛苦) -
[[table]]必须对应[]Struct,不是map[string]Struct;反过来,[table](单表)才对应单个 struct 字段 - 多层嵌套如
[[clusters.nodes]],struct 要写成Clusters []Cluster+Cluster struct { Nodes []Node } - 不确定类型字段(比如插件配置)可先用
map[string]interface{}接,再按需转换,别硬塞 struct
真正麻烦的从来不是“怎么读”,而是“怎么让读出来的值刚好能用”——字段名大小写、标签拼写、数组 vs 单对象、时间格式、混合类型……每个点都可能卡住一小时。动手前,先 cat config.toml | head -20 看一眼真实结构,再对齐 struct,比查文档快得多。

















