go - 如何在保留注释的情况下解析 golang 中的一般 yaml?

标签 go yaml

我正在玩 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/

相关文章:

go - HDFS 客户端拨号 tcp : lookup xxx(my hostname) on 127. 0.0.11:53:没有这样的主机

C 常量上的 Cgo 链接器错误

yaml - Azure CLI - VSTS - Yaml - 似乎只执行了脚本的第一行

c# - Azure DevOps YAML 管道失败并显示 "A sequence was not expected"

java - 从字符串标识符进行多态构造

go - 如何在golang中通过值获取枚举变量名称?

go - Revel session 集群与 Redis

go - stub.GetCallerCertificate()、stub.GetCallerMetadata()、stub.GetPayload() 返回空白

python - 在 ruamel.yaml 中,如何发出带有文字字符串 "null"的 ScalarEvent ?

go - 自定义 UnmarshalYAML,如何在自定义类型上实现 Unmarshaler 接口(interface)