我正在玩 golang yaml v3 库。目标是从带有注释的文件中解析任何 yaml(这意味着我没有预定义的结构),能够在结果树中设置或取消设置任何值并将其写回文件。
但是,我遇到了很奇怪的行为。正如你在下面的代码中看到的,如果传递给 Unmarshal 函数的主要类型是 interface{}
,不保留注释,库使用映射和 slice 来表示 yaml 的结构。另一方面,如果我使用(在这种情况下)[]yaml.Node
结构,它确实在内部将所有节点表示为 yaml.Node
或 []yaml.Node
.这或多或少是我想要的,因为它允许评论保留。然而,这不是一个通用的解决方案,因为至少有两种不同的场景 - YAML 以数组或 map 开头,我不确定如何优雅地处理这两种情况。
您能否指出我正确的方向并详细说明图书馆为什么会这样?
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))
}
最佳答案
On the other hand, if I use (in this case) []yaml.Node structure, it does represent all nodes internally as yaml.Node or []yaml.Node.
那是不准确的。 go-yaml 允许您将结构的任何子树保留为
yaml.Node
可能是为了以后的处理。在此节点内,所有内容都表示为 yaml.Node
,并且作为集合(序列或映射)的节点恰好将其子节点存储为 []yaml.Node
.但是没有节点直接表示为[]yaml.Node
.当您反序列化为
[]yaml.Node
,您将顶级节点反序列化为原生结构( slice ),同时不构造子节点(将 YAML 节点加载到原生结构中的过程在规范中称为构造)。go-yaml 并不真正支持
type Document yaml.Node
但如果你只是这样做
var d yaml.Node
评论也将被保留(
toSlice
显然不再起作用):- # Employee records
martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
现在我们可以看到,评论的位置不同了。这是因为 go-yaml 只存储在
yaml.Node
中表示“在此列表项之前有评论”的列表项。有关评论确切位置的信息丢失了。你应该庆幸你有关于评论的任何信息,因为大多数 YAML 实现很早就废弃了它们,因为规范说评论不能传达内容信息。您可能想阅读 I want to load a YAML file, possibly edit the data, and then dump it again. How can I preserve formatting?其中详细说明了在加载 YAML 文件期间信息丢失的原因、时间和方式。 TL;DR:加载一个 YAML 文件并在保留所有格式的同时将其转储是不可能的(基本上不用自己解析),如果这是你的目标,那么 YAML 对你来说是错误的工具。
关于go - 如何在保留注释的情况下解析 golang 中的一般 yaml?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62062329/