如何解组一个 YAML 数组?

huangapple go评论89阅读模式
英文:

How to unmarshal a yaml array?

问题

我有以下的YAML文件,如果我在在线YAML网站上验证它,它说它是有效的:

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

然后,我编写了一段代码来解析这个YAML文件中的值12,代码如下:

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)
}

它正常工作如下:

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

1
2

问题是什么?

你可以看到,我在这里做了一个变通的方法,即在原始的YAML之前添加了一个特殊的foobar键,然后我可以使用type Configs struct来解析它:

- foo: 1
  bar: 2

foobar:
- foo: 1
  bar: 2

那么,如果我不使用这个变通方法来添加前缀foobar:,我该如何直接解析- {"foo": 1, "bar": 2}

英文:

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}?

答案1

得分: 5

由于您的YAML数据是一个数组,因此将其解组为Config结构的数组。

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)
}

输出:

[{1 2}]

Go Playground上尝试。

英文:

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

答案2

得分: 1

由于问题已经得到解答,我想我可以在讨论中添加一些内容:

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

这里的data变量,按照你的写法,将包括每行开头的缩进。

这里的额外缩进将与所有实际的yaml数据一起传递给yaml.Unmarshal(),这可能会导致问题,因为**gofmt会将代码格式化为使用TAB作为缩进**,而在yaml中禁止使用TAB缩进(https://stackoverflow.com/a/19976827/7509248)。

如果你在yaml中使用TAB作为缩进,尝试解组时会抛出类似以下错误的异常:

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

最好的做法是从单独的文件中加载data,以避免此类问题。

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

输出:

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

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}]

huangapple
  • 本文由 发表于 2021年8月10日 17:21:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/68724167.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定