Как разобрать общий yaml в golang с сохранением комментариев? - PullRequest
2 голосов
/ 28 мая 2020

Играю с библиотекой golang yaml v3. Цель состоит в том, чтобы проанализировать любой yaml (это означает, что у меня нет предопределенной структуры) из файла с комментариями, чтобы иметь возможность устанавливать или отменять любое значение в результирующем дереве и записывать его обратно в файл.

Однако , Я столкнулся с довольно странным поведением. Как вы можете видеть в приведенном ниже коде, если основной тип, переданный в функцию Unmarshal, равен interface{}, никакие комментарии не сохраняются, и библиотека использует карты и фрагменты для представления структуры yaml. С другой стороны, если я использую (в данном случае) структуру []yaml.Node, она действительно представляет все узлы внутри как yaml.Node или []yaml.Node. Это более или менее то, что я хочу, потому что это позволяет сохранять комментарии. Однако это не общее решение, потому что существует как минимум два разных сценария ios - либо 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 Ответ

1 голос
/ 28 мая 2020

С другой стороны, если я использую (в данном случае) структуру [] yaml.Node, она действительно представляет все узлы внутри как yaml.Node или [] yaml.Node.

Это неточно. go -yaml позволяет вам оставить любое поддерево вашей структуры как yaml.Node, возможно, для последующей обработки. Внутри этого узла все представлено как yaml.Node, а узел, который является коллекцией (последовательностью или отображением), просто хранит свои дочерние элементы как []yaml.Node. Но ни один узел не представлен напрямую как []yaml.Node.

Когда вы десериализуете в []yaml.Node, вы десериализуете узел верхнего уровня в собственную структуру (срез), оставляя дочерние элементы несформированными (процесс загрузки узел YAML в нативной структуре называется конструкция в spe c).

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 отбрасывают их намного раньше, поскольку spe c говорит, что комментарии не должны передавать информацию о содержимом.

Возможно, вы захотите для чтения Я хочу загрузить файл YAML, возможно, отредактировать данные, а затем снова сбросить его. Как сохранить форматирование? подробно описывает, почему, когда и как информация теряется во время загрузки файла YAML. TL; DR: Невозможно (без самостоятельного анализа) загрузить файл YAML и выгрузить его обратно с сохранением всего форматирования, и если это ваша цель, YAML - неподходящий инструмент для вас.

...