atline
atline

Reputation: 31604

How to unmarshal a yaml array?

I have next yaml, if I validate it in online yaml website, it said it's valid:

- {"foo": "1", "bar": "2"}

Then, I write a code to parse the value 1 and 2 from this yaml as next:

test.go:

package main
import "gopkg.in/yaml.v2"
import "fmt"

type Config struct {
    Foo string
    Bar string
}

type Configs struct {
    Cfgs []Config `foobar`
}

func main() {
    //var data = `
    //  foobar:
    //  - foo: 1
    //    bar: 2
    //`
    var data = `
      - foo: 1
        bar: 2
    `

    source := []byte("foobar:" + data)
    var configs Configs
    err := yaml.Unmarshal(source, &configs)
    if err != nil {
        fmt.Println("error: %v", err)
    }
    fmt.Printf("--- configs:\n%v\n\n", configs)
    fmt.Println(configs.Cfgs[0].Foo)
    fmt.Println(configs.Cfgs[0].Bar)
}

It works ok as next:

shubuntu1@shubuntu1:~/20210810$ go run test.go
--- configs:
{[{1 2}]}

1
2

What's the problem?

You could see I made a workaround here as next to add special foobar key before the original yaml, then I could use type Configs struct to unmarshal it:

From

- foo: 1
  bar: 2

to

foobar:
- foo: 1
  bar: 2

So, if I don't use the workaround to add a prefix foobar:, how could I directly parse - {"foo": 1, "bar": 2}?

Upvotes: 4

Views: 10730

Answers (2)

Miigon
Miigon

Reputation: 879

Since the question has already been answered, I thought I might add something to the discussion:

    var data = `
      - foo: 1
        bar: 2
    `

The data variable here, the way you wrote it, will include the indentation at the start of each line.

The extra indentation here WILL be passed into yaml.Unmarshal() alongside all the actual yaml data, which can mess things up since gofmt formats your code to use TAB as indentation and TAB indentation is forbidden in yaml (https://stackoverflow.com/a/19976827/7509248).

If you use tab as indentation in yaml, an error like this will be thrown when trying to unmarshal it:

yaml: line 1: found a tab character that violates indentation

Preferably, you want to load data from a separate file to avoid such issue.

package main

import (
    "fmt"
    "io/ioutil"

    "gopkg.in/yaml.v2"
)

type Config struct {
    Foo string
    Bar string
}

func main() {
    var configs []Config

    source, err := ioutil.ReadFile("config.yaml")
    if err != nil {
        fmt.Printf("failed reading config file: %v\n", err)
    }

    err = yaml.Unmarshal(source, &configs)
    if err != nil {
        fmt.Printf("error: %v\n", err)
    }

    fmt.Printf("config:\n%+v\n", configs)
}

config.yaml:

- foo: 1
  bar: 2

output:

config:
[{Foo:1 Bar:2}]

Upvotes: 2

Kamol Hasan
Kamol Hasan

Reputation: 13496

Since your YAML data is an array, unmarshal it to an array of Config structure.

package main

import (
    "fmt"

    "gopkg.in/yaml.v2"
)

type Config struct {
    Foo string
    Bar string
}

func main() {
    var configs []Config

    var data = `
      - foo: 1
        bar: 2
    `

    err := yaml.Unmarshal([]byte(data), &configs)
    if err != nil {
        panic(err)
    }

    fmt.Println(configs)
}

Output:

[{1 2}]

Try on - Go Playground

Upvotes: 5

Related Questions