如何使用保留的注释来解析golang中的一般Yaml?

时间:2020-05-28 10:19:08

标签: go yaml

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

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

}

1 个答案:

答案 0 :(得分:1)

另一方面,如果使用(在这种情况下)[] yaml.Node结构,则它会在内部将所有节点都表示为yaml.Node或[] yaml.Node。

那是不准确的。 go-yaml使您可以将结构的任何子树保留为yaml.Node,以便以后处理。在此节点内,所有内容都表示为yaml.Node,而作为集合(序列或映射)的节点恰好将其子级存储为[]yaml.Node。但是没有节点直接表示为[]yaml.Node

反序列化为[]yaml.Node时,您将顶级节点反序列化为本机结构(切片),而子级未构造(将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“在该列表项目之前已经有注释” 的 Task<Boolean> task = new Task<Boolean>() { @Override protected Boolean call() throws Exception { Thread.sleep(300) // needs this to make sure the table is displayed table.refresh(); table.scrollTo(0); return true; } }; new Thread(task).start(); 中。有关注释的确切位置的信息已丢失。您应该感谢您拥有有关注释的任何信息,因为大多数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对您来说是错误的工具。

相关问题