Go unmarshal yaml and preserve order

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

Go unmarshal yaml and preserve order

问题

假设我有以下的 YAML 文件:

key1:
    - "Value1"
    - "Value2"
key2:
    - "Value3"
    - "Value4"

我想在 Go 中对其进行解析。我可以让 Go 自己决定方案的样子并将其打印出来。

m := make(map[interface{}]interface{})
err := yaml.Unmarshal(yaml_file, &m)
fmt.Println(m)

这将打印出类似以下的内容:
map[key1:[Value1 Value2] key2:[Value3 Value4]]

但是,映射是无序的。对我来说,顺序非常重要,而且我也不想丢失键。

我不能只是创建一个结构体,然后尝试将 YAML 文件解析为该结构体的切片...

type Element struct {
    Key   string
    Value interface{}
}

t := []Element{}
err := yaml.Unmarshal(yaml_file, &t)
if err != nil {
    log.Fatalf("error: %v", err)
}

会打印出 error: yaml: unmarshal errors: line 2: cannot unmarshal !!map into []Element

没有字段标签可以告诉 YAML 解析器将 Key 属性填充为 YAML 键。

除了编写自己的解码器之外,是否有其他方法呢?

英文:

Let's say I have the following yaml file:

key1:
    - "Value1"
    - "Value2"
key2:
    - "Value3"
    - "Value4"

And I want to unmarshal that in Go. I can let Go decide what the scheme should look like and print it out.

m := make(map[interface{}]interface{})
err := yaml.Unmarshal(yaml_file, &m)
fmt.Prinf(m)

This will print something like this:
map[key1:[Value1 Value2] key2:[Value3 Value4]]

But maps are unordered. The order is very important for me tho and I also don't want to loose the keys.

I can't just create a struct and try to unmarshal the yaml file to a slice of that struct...

type Element struct {
    Key   string
    Value interface{}
}

t := []Element{}
err := yaml.Unmarshal(yaml_file, &t)
if err != nil {
	log.Fatalf("error: %v", err)
}

Will print error: yaml: unmarshal errors: line 2: cannot unmarshal !!map into []Element

There is no field tag to tell the yaml parser to fill the Key property with the yaml key.

Is there a different way than writing my own decoder?

答案1

得分: 4

刚刚在我准备发布问题之前找到了答案。

答案是MapSlice!你需要从gopkg导入yaml.v2。(截至我写这篇文章时,MapSlices似乎还没有在yaml.v3中实现)

MapSlices完全实现了我所需要的,并由多个MapItem组成。

type MapItem struct {
    Key, Value interface{}
}

现在只需将其解组为MapSlice。如果你想使用自己的类型进行操作,你总是可以进行转换,但可能需要进行一些类型转换。

m := yaml.MapSlice{}
yaml.Unmarshal(yaml_file, &m)

t := []Element{}
for _, item := range m {
    t = append(t, Element{item.Key.(string), item.Value})
}
英文:

Just found the answer right before I wanted to post the question.

The answer is MapSlice!
You need to import yaml.v2 from gopkg. (As of writing this MapSlices don't seem to be implemented in yaml.v3 yet)

MapSlices implement exactly what I needed and are made of a number of MapItems.

type MapItem struct {
	Key, Value interface{}
}

So now just unmarshal to a MapSlice. If you want to work with your own types you can always convert it but might need to do some typecasting.

m := yaml.MapSlice{}
yaml.Unmarshal(yaml_file, &m)

t := []Element{}
for _, item := range m {
    t = append(t, Element{item.Key.(string), item.Value})
}

huangapple
  • 本文由 发表于 2022年1月20日 04:09:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/70777144.html
匿名

发表评论

匿名网友

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

确定