【问题标题】:How to parse general yaml in golang with comments preserved?如何在保留注释的情况下解析 golang 中的一般 yaml?
【发布时间】:2020-09-15 15:04:46
【问题描述】:

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

但是,我遇到了相当奇怪的行为。正如您在下面的代码中看到的,如果传递给 Unmarshal 函数的主要类型是 interface{},则不保留任何 cmets,并且库使用映射和切片来表示 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))

}

【问题讨论】:

    标签: go yaml


    【解决方案1】:

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

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

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

    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 实现更早地废弃了它们,因为规范规定 cmets 不得传达内容信息。

    您可能需要阅读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 对您来说是错误的工具。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 2016-02-08
      • 2018-10-15
      • 2018-09-23
      • 1970-01-01
      • 2011-05-27
      • 2015-07-08
      相关资源
      最近更新 更多