Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more
I'm trying to unmarshal YAML entries that can be either a string or a list of key: value strings (a map as per Go). I cannot figure out how to get this done sadly. I know I can write my own unmarshaller but that seems to only work with structs.
I have the first part working:
package main
import (
"log"
"gopkg.in/yaml.v2"
type Data struct {
Entry []Entry `yaml:"entries"`
type Entry map[string]string
var dat string = `
entries:
- keya1: val1
keya2: val2
- keyb1: val1
keyb2: val2
- val3`
func main() {
out := Data{}
if err := yaml.Unmarshal([]byte(dat), &out); err != nil {
log.Fatal(err)
log.Printf("%+v", out)
But the - val3 entry causes an error now, obviously. How can I get it to recognise both lists and single string entries?
Thank you
–
–
This has been answered in various ways before, but long story short it is easy unmarshall into an interface and then deal with both cases
type Entry interface{}
for _, entry := range out.Entry {
switch i := entry.(type) {
case string:
log.Printf("i is a string %+v\n", i)
case map[interface{}]interface{}:
log.Printf("i is a map %+v\n", i)
–
This is just a followup to the excellent @Benjamin Kadish answer above, but here's a somewhat more complete version and this uses yaml.v3, which makes it just a bit more obvious. Note that the type of the unmarshalled items is map[string]interface{} instead of map[interface{}]interface{} in yaml v3.
package main
import (
"gopkg.in/yaml.v3"
"log"
type Data struct {
Entry []Entry `yaml:"entries"`
type Entry interface {}
var dat string = `
entries:
- keya1: val1
keya2: val2
- keyb1: val1
keyb2: val2
- val3`
func main() {
out := Data{}
if err := yaml.Unmarshal([]byte(dat), &out); err != nil {
log.Fatal(err)
for _, entry := range out.Entry {
switch i := entry.(type) {
case string:
log.Printf("i is a string: %+v\n", i)
case map[string]interface{}:
log.Printf("i is a map.")
for k,v := range i {
log.Printf("%s=%v\n",k,v)
default:
log.Printf("Type i=%s", i)
As, go is static type language, you can't leave val3 only a list item if you convert it to defined struct. It should be key value pair. E.g. (val3: "") (if you want it empty.)
Here is modified code
package main
import (
"log"
"gopkg.in/yaml.v2"
type Data struct {
Entry []Entry `yaml:"entries"`
type Entry map[string]string
var dat string = `
entries:
- keya1: val1
keya2: val2
- keyb1: val1
keyb2: val2
- val3: ""`
func main() {
out := Data{}
if err := yaml.Unmarshal([]byte(dat), &out); err != nil {
log.Fatal(err)
log.Printf("%+v", out)
And output:
2018/02/02 01:07:36 {Entry:[map[keya1:val1 keya2:val2] map[keyb2:val2 keyb1:val1] map[val3:]]}
–
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.