-
Notifications
You must be signed in to change notification settings - Fork 4
/
unmarshal.go
78 lines (71 loc) · 2.24 KB
/
unmarshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package yamljson
import (
"errors"
"fmt"
"io"
"strings"
"dario.cat/mergo"
"gopkg.in/yaml.v2"
)
// UnmarshalSingleYaml will unmarshal the first yaml doc in a single yaml/json
// string without merging. This form works for any yaml data, not just objects.
func UnmarshalSingleYaml(yamlString string) (interface{}, error) {
results, err := UnmarshalAllYaml(yamlString)
return results[0], err
}
// UnmarshalAllYaml will unmarshal all yaml docs in a single yaml/json
// string without merging. This form works for any yaml data, not just objects.
func UnmarshalAllYaml(yamlString string) ([]interface{}, error) {
var results []interface{}
var err error
decoder := yaml.NewDecoder(strings.NewReader(yamlString))
for err == nil {
var result interface{}
err = decoder.Decode(&result)
if err == nil {
results = append(results, result)
}
}
if errors.Is(err, io.EOF) {
return results, nil
}
if err != nil {
return results, fmt.Errorf("yaml decode: %w", err)
}
return results, nil
}
// UnmarshalYamlInterface will parse all the supplied yaml strings, merge the resulting
// objects, and return the resulting map. If a root node is a list it will be
// converted to an int map prior to merging. An emtpy document returns nil.
func UnmarshalYamlInterface(yamlStrings ...string) (interface{}, error) {
// We collect all the yamls into a base string map so mergo can handle them as
// subnodes for consistentcy (mergo doesn't like conflicting types in root
// nodes)
var allYamls []map[string]interface{}
for _, yamlString := range yamlStrings {
yamls, err := UnmarshalAllYaml(yamlString)
if err != nil {
return nil, err
}
for _, yaml := range yamls {
// We do this to maintain backward compatibility with empty docs being
// treated as an empty map
if yaml != nil {
allYamls = append(allYamls, map[string]interface{}{"root": yaml})
}
}
}
result := make(map[string]interface{})
for _, y := range allYamls {
if err := mergo.Merge(&result, y, mergo.WithOverride); err != nil {
return nil, fmt.Errorf("yaml merge: %w", err)
}
}
r := result["root"]
if r == nil {
// We do this to maintain backward compatibility with empty docs being
// treated as an empty map
return map[interface{}]interface{}{}, nil
}
return r, nil
}