Skip to content

Instantly share code, notes, and snippets.

@packrat386
Created January 5, 2021 23:12
Show Gist options
  • Save packrat386/bd3f9fcbcc89199667542d2518c4e89b to your computer and use it in GitHub Desktop.
Save packrat386/bd3f9fcbcc89199667542d2518c4e89b to your computer and use it in GitHub Desktop.
YAML to JSON is harder than you might think
[acoyle01] wat > go run yamltojson.go
lets turn YAML into JSON
here's the naive data structure
(map[string]interface {}) (len=1) {
(string) (len=3) "top": (map[interface {}]interface {}) (len=4) {
(string) (len=5) "super": (map[interface {}]interface {}) (len=1) {
(string) (len=5) "duper": (map[interface {}]interface {}) (len=1) {
(string) (len=4) "deep": (string) (len=3) "map"
}
},
(string) (len=4) "list": ([]interface {}) (len=2 cap=2) {
(map[interface {}]interface {}) (len=1) {
(string) (len=5) "where": (map[interface {}]interface {}) (len=1) {
(string) (len=3) "the": (string) (len=6) "values"
}
},
(map[interface {}]interface {}) (len=1) {
(string) (len=3) "are": (map[interface {}]interface {}) (len=1) {
(string) (len=4) "yaml": (string) (len=4) "maps"
}
}
},
(string) (len=6) "isnull": (interface {}) <nil>,
(string) (len=7) "numkeys": (map[interface {}]interface {}) (len=2) {
(int) 1: (string) (len=3) "one",
(int) 2: (string) (len=3) "two"
}
}
}
this fails: json: unsupported type: map[interface {}]interface {}
here's the custom data structure
(map[string]*main.JSONValue) (len=1) {
(string) (len=3) "top": (*main.JSONValue)(0xc000010b00)({
contains: (map[string]*main.JSONValue) (len=4) {
(string) (len=6) "isnull": (*main.JSONValue)(<nil>),
(string) (len=7) "numkeys": (*main.JSONValue)(0xc000010b50)({
contains: (map[string]*main.JSONValue) (len=2) {
(string) (len=1) "1": (*main.JSONValue)(0xc000010b80)({
contains: (string) (len=3) "one"
}),
(string) (len=1) "2": (*main.JSONValue)(0xc000010c40)({
contains: (string) (len=3) "two"
})
}
}),
(string) (len=5) "super": (*main.JSONValue)(0xc000010d00)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=5) "duper": (*main.JSONValue)(0xc000010d30)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=4) "deep": (*main.JSONValue)(0xc000010d60)({
contains: (string) (len=3) "map"
})
}
})
}
}),
(string) (len=4) "list": (*main.JSONValue)(0xc000010e20)({
contains: ([]*main.JSONValue) (len=2 cap=2) {
(*main.JSONValue)(0xc000010e50)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=5) "where": (*main.JSONValue)(0xc000010e80)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=3) "the": (*main.JSONValue)(0xc000010eb0)({
contains: (string) (len=6) "values"
})
}
})
}
}),
(*main.JSONValue)(0xc000010f50)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=3) "are": (*main.JSONValue)(0xc000010f80)({
contains: (map[string]*main.JSONValue) (len=1) {
(string) (len=4) "yaml": (*main.JSONValue)(0xc000010fb0)({
contains: (string) (len=4) "maps"
})
}
})
}
})
}
})
}
})
}
this succeeds
{"top":{"isnull":null,"list":[{"where":{"the":"values"}},{"are":{"yaml":"maps"}}],"numkeys":{"1":"one","2":"two"},"super":{"duper":{"deep":"map"}}}}
package main
import (
"encoding/json"
"fmt"
"github.com/davecgh/go-spew/spew"
"gopkg.in/yaml.v2"
)
const document = `
top:
isnull: null
numkeys:
1: one
2: two
super:
duper:
deep: map
list:
- where:
the: values
- are:
yaml: maps
`
func main() {
fmt.Println("lets turn YAML into JSON")
var naive map[string]interface{}
err := yaml.Unmarshal([]byte(document), &naive)
if err != nil {
panic(err)
}
fmt.Println("here's the naive data structure")
spew.Dump(naive)
buf, err := json.Marshal(naive)
if err != nil {
fmt.Println("this fails: ", err)
}
var fixed map[string]*JSONValue
err = yaml.Unmarshal([]byte(document), &fixed)
if err != nil {
panic(err)
}
fmt.Println("here's the custom data structure")
spew.Dump(fixed)
buf, err = json.Marshal(fixed)
if err != nil {
panic(err)
}
fmt.Println("this succeeds")
fmt.Println(string(buf))
}
type JSONValue struct {
contains interface{}
}
func (j *JSONValue) UnmarshalYAML(unmarshal func(interface{}) error) error {
var object map[string]*JSONValue
err := unmarshal(&object)
if err == nil {
j.contains = object
return nil
}
var array []*JSONValue
err = unmarshal(&array)
if err == nil {
j.contains = array
return nil
}
var str string
err = unmarshal(&str)
if err == nil {
j.contains = str
return nil
}
var number float64
err = unmarshal(&number)
if err == nil {
j.contains = number
return nil
}
var boolean bool
err = unmarshal(&boolean)
if err == nil {
j.contains = boolean
return nil
}
var null interface{}
err = unmarshal(&null)
if err == nil && null == nil {
j.contains = nil
return nil
}
return fmt.Errorf("No JSON suitable type found")
}
func (j *JSONValue) MarshalJSON() ([]byte, error) {
return json.Marshal(j.contains)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment