I am playing with golang yaml v3 library. The goal is to parse any yaml (that means that I don't have predefined structure) from file with comments, be able to set or unset any value in the resulting tree and write it back to file.
However, I have encountered quite strange behavior. As you can see in the code below, if the main type passed to the Unmarshal function is interface{}
, no comments are preserved and library uses maps and slices to represent the structure of yaml. 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
. This is more or less what I want, because it allows comment preservation. However, it is not a general solution because there are at least two distinct scenarios - either the YAML starts with an array or with a map and I am not sure how to elegantly deal with both situations.
Could you possibly point me in the right direction and elaborate on why does the library behaves this way?
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))
}