
本文介绍如何在 go 语言中利用 go-yaml/yaml 包批量解析 yaml 文档中的多个同构结构,核心方法是将 yaml 数据组织为列表(sequence),并反序列化为结构体切片。
在 Go 中使用 gopkg.in/yaml.v2(或推荐升级至 github.com/go-yaml/yaml/v3)解析 YAML 时,若原始数据包含多个逻辑上独立但结构相同的对象(如多个配置项、资源定义等),不能直接重复使用单个结构体变量进行多次 Unmarshal —— 因为 YAML 解析器会将重复键名视为覆盖操作(如问题中 first 出现两次,后者覆盖前者),最终仅保留最后一个。
✅ 正确做法:将 YAML 数据建模为 YAML 序列(即 YAML 列表),对应 Go 中的切片([]Container)。
✅ 正确的 YAML 格式(必须使用 - 表示列表项)
- first: first value
second:
nested1: GET
nested2: /bin/bash
nested3: /usr/local/bin/customscript
nested4: 8080
- first: second value
second:
nested1: POST
nested2: /bin/ksh
nested3: /usr/local/bin/customscript2
nested4: 8081✅ 对应的 Go 实现(完整可运行示例)
package main
import (
"fmt"
"log"
"gopkg.in/yaml.v2" // 推荐后续迁移到 github.com/go-yaml/yaml/v3
)
type Container struct {
First string `yaml:"first"`
Second struct {
Nested1 string `yaml:"nested1"`
Nested2 string `yaml:"nested2"`
Nested3 string `yaml:"nested3"`
Nested4 int `yaml:"nested4"`
} `yaml:"second"`
}
var data = `
- first: first value
second:
nested1: GET
nested2: /bin/bash
nested3: /usr/local/bin/customscript
nested4: 8080
- first: second value
second:
nested1: POST
nested2: /bin/ksh
nested3: /usr/local/bin/customscript2
nested4: 8081
`
func main() {
var containers []Container // 注意:声明为切片类型
err := yaml.Unmarshal([]byte(data), &containers)
if err != nil {
log.Fatalf("YAML 解析失败: %v", err)
}
fmt.Printf("--- 共解析 %d 个 Container 实例:\n", len(containers))
for i, c := range containers {
fmt.Printf("[%d] %+v\n", i, c)
}
}? 关键要点说明:
- yaml.Unmarshal 的第二个参数必须是指向切片的指针(&containers),而非单个结构体;
- YAML 中每个对象前必须加 -(破折号),表示该对象是列表的一项;
- 字段标签(如 `yaml:"first"`)强烈建议显式声明,避免大小写/下划线不一致导致解析失败;
- 若需兼容更复杂场景(如混合类型、动态字段),可考虑先解析为 []map[string]interface{} 再手动转换,但结构体切片方案更安全、类型明确、性能更优。
⚠️ 注意事项:
立即学习“go语言免费学习笔记(深入)”;
- 原始 YAML 若无法修改格式(例如来自第三方 API 且固定为多文档流 --- 分隔),则需改用 yaml.Decoder 配合 Decode() 循环读取(见进阶用法);
- yaml.v2 已归档,新项目请优先使用 github.com/go-yaml/yaml/v3 —— 其 API 更稳定,对多文档、锚点、自定义标签支持更好;
- 解析错误时,err 通常包含具体行号与字段路径,便于快速定位 YAML 格式问题。
通过将数据建模为 YAML 列表并匹配 Go 切片,即可简洁、高效、类型安全地批量加载多个结构体实例——这是 Go + YAML 开发中的标准实践。










