如何解析golang中的通用yaml并保留注释?

Kos*_*ahb 7 yaml go

我正在使用 golang yaml v3 库。目标是从带有注释的文件中解析任何 yaml(这意味着我没有预定义的结构),能够设置或取消设置结果树中的任何值并将其写回文件。

然而,我遇到了相当奇怪的行为。正如您在下面的代码中看到的,如果传递给 Unmarshal 函数的主要类型是interface{},则不会保留任何注释,并且库使用映射和切片来表示 yaml 的结构。另一方面,如果我使用(在本例中)[]yaml.Node结构,它确实在内部将所有节点表示为yaml.Nodeor []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)

fly*_*lyx 6

\n

另一方面,如果我使用(在本例中)[]yaml.Node 结构,它确实在内部将所有节点表示为 yaml.Node 或 []yaml.Node。

\n
\n\n

这是不准确的。go-yaml 允许您尽可能保留结构的任何子树yaml.Node以供以后处理。在该节点内,所有内容都表示为yaml.Node,并且作为集合(序列或映射)的节点恰好将其子节点存储为[]yaml.Node。但没有节点直接表示为[]yaml.Node

\n\n

当您反序列化为 时[]yaml.Node,您将顶级节点反序列化为本机结构(切片),同时保留子节点未构造(将 YAML 节点加载到本机结构的过程在规范中称为构造)。

\n\n

go-yaml 并不真正支持

\n\n
type Document yaml.Node\n
Run Code Online (Sandbox Code Playgroud)\n\n

但如果你这样做

\n\n
var d yaml.Node\n
Run Code Online (Sandbox Code Playgroud)\n\n

评论也将被保留(toSlice显然不再起作用):

\n\n
- # 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 实现很早就废弃了它们,因为规范规定注释不得传达内容信息。

\n\n

您可能想阅读我想加载 YAML 文件,可能编辑数据,然后再次转储它。如何保留格式?其中详细介绍了在加载 YAML 文件期间信息丢失的原因、时间和方式。TL;DR:不可能(基本上不自己解析)加载 YAML 文件并将其转储回来,同时保留所有格式,如果这是您的目标,那么 YAML 对您来说是错误的工具。

\n