package mapping import ( "encoding/json" "errors" "io" "io/ioutil" "gopkg.in/yaml.v2" ) // To make .json & .yaml consistent, we just use json as the tag key. const yamlTagKey = "json" var ( // ErrUnsupportedType is an error that indicates the config format is not supported. ErrUnsupportedType = errors.New("only map-like configs are suported") yamlUnmarshaler = NewUnmarshaler(yamlTagKey) ) // UnmarshalYamlBytes unmarshals content into v. func UnmarshalYamlBytes(content []byte, v interface{}) error { return unmarshalYamlBytes(content, v, yamlUnmarshaler) } // UnmarshalYamlReader unmarshals content from reader into v. func UnmarshalYamlReader(reader io.Reader, v interface{}) error { return unmarshalYamlReader(reader, v, yamlUnmarshaler) } func unmarshalYamlBytes(content []byte, v interface{}, unmarshaler *Unmarshaler) error { var o interface{} if err := yamlUnmarshal(content, &o); err != nil { return err } if m, ok := o.(map[string]interface{}); ok { return unmarshaler.Unmarshal(m, v) } return ErrUnsupportedType } func unmarshalYamlReader(reader io.Reader, v interface{}, unmarshaler *Unmarshaler) error { content, err := ioutil.ReadAll(reader) if err != nil { return err } return unmarshalYamlBytes(content, v, unmarshaler) } // yamlUnmarshal YAML to map[string]interface{} instead of map[interface{}]interface{}. func yamlUnmarshal(in []byte, out interface{}) error { var res interface{} if err := yaml.Unmarshal(in, &res); err != nil { return err } *out.(*interface{}) = cleanupMapValue(res) return nil } func cleanupInterfaceMap(in map[interface{}]interface{}) map[string]interface{} { res := make(map[string]interface{}) for k, v := range in { res[Repr(k)] = cleanupMapValue(v) } return res } func cleanupInterfaceNumber(in interface{}) json.Number { return json.Number(Repr(in)) } func cleanupInterfaceSlice(in []interface{}) []interface{} { res := make([]interface{}, len(in)) for i, v := range in { res[i] = cleanupMapValue(v) } return res } func cleanupMapValue(v interface{}) interface{} { switch v := v.(type) { case []interface{}: return cleanupInterfaceSlice(v) case map[interface{}]interface{}: return cleanupInterfaceMap(v) case bool, string: return v case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: return cleanupInterfaceNumber(v) default: return Repr(v) } }