I am trying to read a yaml file that has an array of objects
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.docs)
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 file
DocumentationInfo:
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"}
error
2023/06/27 13:44:44 Unmarshal: yaml: unmarshal errors:
line 1: cannot unmarshal !!map into []config.Document
I am not sure if the issue is the yaml file syntax because it is similar to json, however cross referencing documentation it looks correct.
Any advice would be appreciated…
>Solution :
You have defined the outermost container as a map with a document key containing an array of Documents:
type DocumentationInfo struct {
docs []Document `yaml:"document"`
}
But that is not the structure of your input data, which looks like this:
DocumentationInfo:
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"}
The outer element is a map that contains a DocumentationInfo key (and the value of that key is your map with the document key). You would need to redefine your types like this (I’ve turned this into package main so I can run it locally for testing):
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"gopkg.in/yaml.v3"
)
type DocumentationInfoFile struct {
DocumentationInfo DocumentationInfo `yaml:"DocumentationInfo"`
}
type DocumentationInfo struct {
Docs []Document `yaml:"document"`
}
type Document struct {
Title string `yaml:"title"`
FileName string `yaml:"fileName"`
}
func (docinfo *DocumentationInfoFile) 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, docinfo)
if err != nil {
log.Fatalf("Unmarshal: %v", err)
}
docmap := make(map[string]Document)
for _, doc := range docinfo.DocumentationInfo.Docs {
docmap[doc.FileName] = doc
}
return docmap
}
func NewDocumentConfig() *DocumentationInfoFile {
return &DocumentationInfoFile{}
}
func main() {
d := NewDocumentConfig()
docmap := d.Init()
fmt.Printf("%+v\n", docmap)
}
Running the above code produces:
map[bar.md:{Title:I am an bar title FileName:bar.md} foo.md:{Title:I am an foo title FileName:foo.md} nice.md:{Title:I am an nice title FileName:nice.md} test.md:{Title:I am an test title FileName:test.md}]