我正在使用 golang yaml v3 库。目标是从带有注释的文件中解析任何 yaml(这意味着我没有预定义的结构),能够设置或取消设置结果树中的任何值并将其写回文件。
然而,我遇到了相当奇怪的行为。正如您在下面的代码中看到的,如果传递给 Unmarshal 函数的主要类型是interface{}
,则不会保留任何注释,并且库使用映射和切片来表示 yaml 的结构。另一方面,如果我使用(在本例中)[]yaml.Node
结构,它确实在内部将所有节点表示为yaml.Node
or []yaml.Node
。这或多或少是我想要的,因为它允许保留评论。然而,它不是一个通用的解决方案,因为至少有两种不同的场景 - YAML 以数组开头或以映射开头,我不确定如何优雅地处理这两种情况。
您能否为我指出正确的方向并详细说明为什么图书馆会这样做?
package main
import (
"fmt"
"reflect"
"gopkg.in/yaml.v3"
)
type Document interface{} // change this to []yaml.Node and it will work with comments // change it to yaml.Node and it will not work
var data string = ` # Employee records
- martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
`
func toSlice(slice interface{}) []interface{} {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("InterfaceSlice() given a non-slice type")
}
ret := make([]interface{}, s.Len())
for i:=0; i<s.Len(); i++ {
ret[i] = s.Index(i).Interface()
}
return ret
}
func main() {
var d Document
err := yaml.Unmarshal([]byte(data), &d)
if err != nil {
panic(err)
}
slice := toSlice(d)
fmt.Println(reflect.ValueOf(slice[0]).Kind())
fmt.Println(reflect.TypeOf(d))
fmt.Println(reflect.ValueOf(d).Kind())
output, err := yaml.Marshal(&d)
if err != nil {
panic(err)
}
fmt.Println(string(output))
}
Run Code Online (Sandbox Code Playgroud)
\n\n\n另一方面,如果我使用(在本例中)[]yaml.Node 结构,它确实在内部将所有节点表示为 yaml.Node 或 []yaml.Node。
\n
这是不准确的。go-yaml 允许您尽可能保留结构的任何子树yaml.Node
以供以后处理。在该节点内,所有内容都表示为yaml.Node
,并且作为集合(序列或映射)的节点恰好将其子节点存储为[]yaml.Node
。但没有节点直接表示为[]yaml.Node
。
当您反序列化为 时[]yaml.Node
,您将顶级节点反序列化为本机结构(切片),同时保留子节点未构造(将 YAML 节点加载到本机结构的过程在规范中称为构造)。
go-yaml 并不真正支持
\n\ntype Document yaml.Node\n
Run Code Online (Sandbox Code Playgroud)\n\n但如果你这样做
\n\nvar d yaml.Node\n
Run Code Online (Sandbox Code Playgroud)\n\n评论也将被保留(toSlice
显然不再起作用):
- # Employee records\n martin:\n name: Martin D\'vloper\n job: Developer\n skills:\n - python\n - perl\n - pascal\n- tabitha:\n name: Tabitha Bitumen\n job: Developer\n skills:\n - lisp\n - fortran\n - erlang\n
Run Code Online (Sandbox Code Playgroud)\n\n现在我们可以看到,评论的立场有所不同。这是因为 go-yaml 只存储在yaml.Node
表示列表项\xe2\x80\x9ethere 在此列表项 \xe2\x80\x9c 之前已注释的列表项中。有关评论确切位置的信息丢失了。您应该庆幸您拥有有关注释的任何信息,因为大多数 YAML 实现很早就废弃了它们,因为规范规定注释不得传达内容信息。
您可能想阅读我想加载 YAML 文件,可能编辑数据,然后再次转储它。如何保留格式?其中详细介绍了在加载 YAML 文件期间信息丢失的原因、时间和方式。TL;DR:不可能(基本上不自己解析)加载 YAML 文件并将其转储回来,同时保留所有格式,如果这是您的目标,那么 YAML 对您来说是错误的工具。
\n