GoLang读取YAML文件对象数组遇Unmarshal错误求助
解决Go解析YAML对象数组的错误
问题分析
错误cannot unmarshal !!map into []config.Document的核心原因有三点:
- Go结构体的私有字段(首字母小写)无法被yaml包访问
- Unmarshal目标类型不匹配:试图将顶层为map结构的YAML数据直接解析到切片类型的
c.docs - YAML结构与Go结构体的映射关系不匹配
具体修复步骤
1. 导出结构体字段
Go中只有首字母大写的字段才能被外部包(如yaml.v3)序列化/反序列化,修改结构体定义:
type DocumentationInfo struct { Docs []Document `yaml:"document"` // 首字母大写导出,yaml标签匹配YAML中的document键 } type Document struct { Title string `yaml:"title"` // 首字母大写 FileName string `yaml:"filename"` // 首字母大写,匹配YAML中的filename键(注意小写) }
2. 修正Unmarshal的目标对象
原代码直接将YAML解析到切片c.docs,但YAML顶层是包含DocumentationInfo键的map,应改为解析到整个DocumentationInfo结构体实例:
// 错误写法:err = yaml.Unmarshal(yamlFile, c.docs) err = yaml.Unmarshal(yamlFile, c) // 改为解析到结构体实例c
3. 调整YAML结构(或结构体标签)
原YAML顶层键为DocumentationInfo,与结构体映射不匹配,推荐选择更简洁的方案A:
选项A:修改YAML文件,去掉顶层的DocumentationInfo键
调整后的YAML内容:
document: - {fileName: "foo.md", title: "I am an foo title"} - {fileName: "test.md", title: "I am an test title"} - {fileName: "bar.md", title: "I am an bar title"} - {fileName: "nice.md", title: "I am an nice title"}
选项B(可选):新增顶层结构体匹配YAML结构
如果不想修改YAML,可新增一个顶层结构体:
type RootConfig struct { DocInfo DocumentationInfo `yaml:"DocumentationInfo"` }
然后在Init方法中解析:
var root RootConfig err = yaml.Unmarshal(yamlFile, &root) if err != nil { log.Fatalf("Unmarshal: %v", err) } c.Docs = root.DocInfo.Docs
4. 修正遍历逻辑
字段导出后,遍历需使用大写的字段名:
for _, doc := range c.Docs { docInfo[doc.FileName] = doc }
完整修复后的代码示例
package config import ( "gopkg.in/yaml.v3" "log" "os" "path/filepath" "runtime" ) type DocumentationInfo struct { Docs []Document `yaml:"document"` } type Document struct { Title string `yaml:"title"` FileName string `yaml:"filename"` } func (c *DocumentationInfo) Init() map[string]Document { _, b, _, _ := runtime.Caller(0) basepath := filepath.Dir(b) yamlFile, err := os.ReadFile(basepath + "/documentation.yaml") if err != nil { log.Printf("yamlFile.Get err #%v ", err) } err = yaml.Unmarshal(yamlFile, c) if err != nil { log.Fatalf("Unmarshal: %v", err) } docInfo := make(map[string]Document) for _, doc := range c.Docs { docInfo[doc.FileName] = doc } return docInfo } func NewDocumentConfig() *DocumentationInfo { return &DocumentationInfo{} }
对应的YAML文件(选项A):
document: - {fileName: "foo.md", title: "I am an foo title"} - {fileName: "test.md", title: "I am an test title"} - {fileName: "bar.md", title: "I am an bar title"} - {fileName: "nice.md", title: "I am an nice title"}
内容的提问来源于stack exchange,提问作者Mike3355
相关产品推荐
相关产品推荐

