添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

That's not a map, that's a slice. A slice is a list, a map is a set of key-value pairs. – Adrian Feb 1 '18 at 18:38 I'm not sure what part you're referring to, but the part I'm referring to is definitely a map (key value parts). -- type Entry map[string]string. I guess you are referring to the entries part, which is a list. Basically my list can contain both maps and strings. – Naatan Feb 1 '18 at 18:47

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)
                Much simpler than what I was thinking (I was writing an unmarshaller). Thank you!
– Naatan
                Feb 1 '18 at 19:50

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:]]}
                This just isn't true, it just means the code parsing the yaml needs to be typed properly. The input format should not affect this beyond making it a bit more challenging to parse.
– Naatan
                Feb 1 '18 at 19:50
                Yes, I thought, you want to convert to your struct. So, if struct is okay, then needs to change yaml :)
– Abu Hanifa
                Feb 1 '18 at 20:05
        

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.