├── .github └── workflows │ └── test.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── decode_hooks.go ├── decode_hooks_test.go ├── error.go ├── go.mod ├── mapstructure.go ├── mapstructure_benchmark_test.go ├── mapstructure_bugs_test.go ├── mapstructure_examples_test.go └── mapstructure_test.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go-version: [1.18.x] 8 | os: [ubuntu-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - name: Install Go 12 | uses: actions/setup-go@v2 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | - name: Test 18 | run: go test ./... 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.5.1 2 | 3 | * Wrap errors so they're compatible with `errors.Is` and `errors.As` [GH-282] 4 | * Fix map of slices not decoding properly in certain cases. [GH-266] 5 | 6 | ## 1.5.0 7 | 8 | * New option `IgnoreUntaggedFields` to ignore decoding to any fields 9 | without `mapstructure` (or the configured tag name) set [GH-277] 10 | * New option `ErrorUnset` which makes it an error if any fields 11 | in a target struct are not set by the decoding process. [GH-225] 12 | * New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240] 13 | * Decoding to slice from array no longer crashes [GH-265] 14 | * Decode nested struct pointers to map [GH-271] 15 | * Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280] 16 | * Fix issue where fields with `,omitempty` would sometimes decode 17 | into a map with an empty string key [GH-281] 18 | 19 | ## 1.4.3 20 | 21 | * Fix cases where `json.Number` didn't decode properly [GH-261] 22 | 23 | ## 1.4.2 24 | 25 | * Custom name matchers to support any sort of casing, formatting, etc. for 26 | field names. [GH-250] 27 | * Fix possible panic in ComposeDecodeHookFunc [GH-251] 28 | 29 | ## 1.4.1 30 | 31 | * Fix regression where `*time.Time` value would be set to empty and not be sent 32 | to decode hooks properly [GH-232] 33 | 34 | ## 1.4.0 35 | 36 | * A new decode hook type `DecodeHookFuncValue` has been added that has 37 | access to the full values. [GH-183] 38 | * Squash is now supported with embedded fields that are struct pointers [GH-205] 39 | * Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206] 40 | 41 | ## 1.3.3 42 | 43 | * Decoding maps from maps creates a settable value for decode hooks [GH-203] 44 | 45 | ## 1.3.2 46 | 47 | * Decode into interface type with a struct value is supported [GH-187] 48 | 49 | ## 1.3.1 50 | 51 | * Squash should only squash embedded structs. [GH-194] 52 | 53 | ## 1.3.0 54 | 55 | * Added `",omitempty"` support. This will ignore zero values in the source 56 | structure when encoding. [GH-145] 57 | 58 | ## 1.2.3 59 | 60 | * Fix duplicate entries in Keys list with pointer values. [GH-185] 61 | 62 | ## 1.2.2 63 | 64 | * Do not add unsettable (unexported) values to the unused metadata key 65 | or "remain" value. [GH-150] 66 | 67 | ## 1.2.1 68 | 69 | * Go modules checksum mismatch fix 70 | 71 | ## 1.2.0 72 | 73 | * Added support to capture unused values in a field using the `",remain"` value 74 | in the mapstructure tag. There is an example to showcase usage. 75 | * Added `DecoderConfig` option to always squash embedded structs 76 | * `json.Number` can decode into `uint` types 77 | * Empty slices are preserved and not replaced with nil slices 78 | * Fix panic that can occur in when decoding a map into a nil slice of structs 79 | * Improved package documentation for godoc 80 | 81 | ## 1.1.2 82 | 83 | * Fix error when decode hook decodes interface implementation into interface 84 | type. [GH-140] 85 | 86 | ## 1.1.1 87 | 88 | * Fix panic that can happen in `decodePtr` 89 | 90 | ## 1.1.0 91 | 92 | * Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] 93 | * Support struct to struct decoding [GH-137] 94 | * If source map value is nil, then destination map value is nil (instead of empty) 95 | * If source slice value is nil, then destination slice value is nil (instead of empty) 96 | * If source pointer is nil, then destination pointer is set to nil (instead of 97 | allocated zero value of type) 98 | 99 | ## 1.0.0 100 | 101 | * Initial tagged stable release. 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mitchell Hashimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure) 2 | 3 | mapstructure is a Go library for decoding generic map values to structures 4 | and vice versa, while providing helpful error handling. 5 | 6 | This library is most useful when decoding values from some data stream (JSON, 7 | Gob, etc.) where you don't _quite_ know the structure of the underlying data 8 | until you read a part of it. You can therefore read a `map[string]interface{}` 9 | and use this library to decode it into the proper underlying native Go 10 | structure. 11 | 12 | ## Installation 13 | 14 | Standard `go get`: 15 | 16 | ``` 17 | $ go get github.com/mitchellh/mapstructure 18 | ``` 19 | 20 | ## Usage & Example 21 | 22 | For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure). 23 | 24 | The `Decode` function has examples associated with it there. 25 | 26 | ## But Why?! 27 | 28 | Go offers fantastic standard libraries for decoding formats such as JSON. 29 | The standard method is to have a struct pre-created, and populate that struct 30 | from the bytes of the encoded format. This is great, but the problem is if 31 | you have configuration or an encoding that changes slightly depending on 32 | specific fields. For example, consider this JSON: 33 | 34 | ```json 35 | { 36 | "type": "person", 37 | "name": "Mitchell" 38 | } 39 | ``` 40 | 41 | Perhaps we can't populate a specific structure without first reading 42 | the "type" field from the JSON. We could always do two passes over the 43 | decoding of the JSON (reading the "type" first, and the rest later). 44 | However, it is much simpler to just decode this into a `map[string]interface{}` 45 | structure, read the "type" key, then use something like this library 46 | to decode it into the proper structure. 47 | -------------------------------------------------------------------------------- /decode_hooks.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "encoding" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns 15 | // it into the proper DecodeHookFunc type, such as DecodeHookFuncType. 16 | func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { 17 | // Create variables here so we can reference them with the reflect pkg 18 | var f1 DecodeHookFuncType 19 | var f2 DecodeHookFuncKind 20 | var f3 DecodeHookFuncValue 21 | 22 | // Fill in the variables into this interface and the rest is done 23 | // automatically using the reflect package. 24 | potential := []interface{}{f1, f2, f3} 25 | 26 | v := reflect.ValueOf(h) 27 | vt := v.Type() 28 | for _, raw := range potential { 29 | pt := reflect.ValueOf(raw).Type() 30 | if vt.ConvertibleTo(pt) { 31 | return v.Convert(pt).Interface() 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | 38 | // DecodeHookExec executes the given decode hook. This should be used 39 | // since it'll naturally degrade to the older backwards compatible DecodeHookFunc 40 | // that took reflect.Kind instead of reflect.Type. 41 | func DecodeHookExec( 42 | raw DecodeHookFunc, 43 | from reflect.Value, to reflect.Value) (interface{}, error) { 44 | 45 | switch f := typedDecodeHook(raw).(type) { 46 | case DecodeHookFuncType: 47 | return f(from.Type(), to.Type(), from.Interface()) 48 | case DecodeHookFuncKind: 49 | return f(from.Kind(), to.Kind(), from.Interface()) 50 | case DecodeHookFuncValue: 51 | return f(from, to) 52 | default: 53 | return nil, errors.New("invalid decode hook signature") 54 | } 55 | } 56 | 57 | // ComposeDecodeHookFunc creates a single DecodeHookFunc that 58 | // automatically composes multiple DecodeHookFuncs. 59 | // 60 | // The composed funcs are called in order, with the result of the 61 | // previous transformation. 62 | func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { 63 | return func(f reflect.Value, t reflect.Value) (interface{}, error) { 64 | var err error 65 | data := f.Interface() 66 | 67 | newFrom := f 68 | for _, f1 := range fs { 69 | data, err = DecodeHookExec(f1, newFrom, t) 70 | if err != nil { 71 | return nil, err 72 | } 73 | newFrom = reflect.ValueOf(data) 74 | } 75 | 76 | return data, nil 77 | } 78 | } 79 | 80 | // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. 81 | // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. 82 | func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { 83 | return func(a, b reflect.Value) (interface{}, error) { 84 | var allErrs string 85 | var out interface{} 86 | var err error 87 | 88 | for _, f := range ff { 89 | out, err = DecodeHookExec(f, a, b) 90 | if err != nil { 91 | allErrs += err.Error() + "\n" 92 | continue 93 | } 94 | 95 | return out, nil 96 | } 97 | 98 | return nil, errors.New(allErrs) 99 | } 100 | } 101 | 102 | // StringToSliceHookFunc returns a DecodeHookFunc that converts 103 | // string to []string by splitting on the given sep. 104 | func StringToSliceHookFunc(sep string) DecodeHookFunc { 105 | return func( 106 | f reflect.Kind, 107 | t reflect.Kind, 108 | data interface{}) (interface{}, error) { 109 | if f != reflect.String || t != reflect.Slice { 110 | return data, nil 111 | } 112 | 113 | raw := data.(string) 114 | if raw == "" { 115 | return []string{}, nil 116 | } 117 | 118 | return strings.Split(raw, sep), nil 119 | } 120 | } 121 | 122 | // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts 123 | // strings to time.Duration. 124 | func StringToTimeDurationHookFunc() DecodeHookFunc { 125 | return func( 126 | f reflect.Type, 127 | t reflect.Type, 128 | data interface{}) (interface{}, error) { 129 | if f.Kind() != reflect.String { 130 | return data, nil 131 | } 132 | if t != reflect.TypeOf(time.Duration(5)) { 133 | return data, nil 134 | } 135 | 136 | // Convert it by parsing 137 | return time.ParseDuration(data.(string)) 138 | } 139 | } 140 | 141 | // StringToIPHookFunc returns a DecodeHookFunc that converts 142 | // strings to net.IP 143 | func StringToIPHookFunc() DecodeHookFunc { 144 | return func( 145 | f reflect.Type, 146 | t reflect.Type, 147 | data interface{}) (interface{}, error) { 148 | if f.Kind() != reflect.String { 149 | return data, nil 150 | } 151 | if t != reflect.TypeOf(net.IP{}) { 152 | return data, nil 153 | } 154 | 155 | // Convert it by parsing 156 | ip := net.ParseIP(data.(string)) 157 | if ip == nil { 158 | return net.IP{}, fmt.Errorf("failed parsing ip %v", data) 159 | } 160 | 161 | return ip, nil 162 | } 163 | } 164 | 165 | // StringToIPNetHookFunc returns a DecodeHookFunc that converts 166 | // strings to net.IPNet 167 | func StringToIPNetHookFunc() DecodeHookFunc { 168 | return func( 169 | f reflect.Type, 170 | t reflect.Type, 171 | data interface{}) (interface{}, error) { 172 | if f.Kind() != reflect.String { 173 | return data, nil 174 | } 175 | if t != reflect.TypeOf(net.IPNet{}) { 176 | return data, nil 177 | } 178 | 179 | // Convert it by parsing 180 | _, net, err := net.ParseCIDR(data.(string)) 181 | return net, err 182 | } 183 | } 184 | 185 | // StringToTimeHookFunc returns a DecodeHookFunc that converts 186 | // strings to time.Time. 187 | func StringToTimeHookFunc(layout string) DecodeHookFunc { 188 | return func( 189 | f reflect.Type, 190 | t reflect.Type, 191 | data interface{}) (interface{}, error) { 192 | if f.Kind() != reflect.String { 193 | return data, nil 194 | } 195 | if t != reflect.TypeOf(time.Time{}) { 196 | return data, nil 197 | } 198 | 199 | // Convert it by parsing 200 | return time.Parse(layout, data.(string)) 201 | } 202 | } 203 | 204 | // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to 205 | // the decoder. 206 | // 207 | // Note that this is significantly different from the WeaklyTypedInput option 208 | // of the DecoderConfig. 209 | func WeaklyTypedHook( 210 | f reflect.Kind, 211 | t reflect.Kind, 212 | data interface{}) (interface{}, error) { 213 | dataVal := reflect.ValueOf(data) 214 | switch t { 215 | case reflect.String: 216 | switch f { 217 | case reflect.Bool: 218 | if dataVal.Bool() { 219 | return "1", nil 220 | } 221 | return "0", nil 222 | case reflect.Float32: 223 | return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil 224 | case reflect.Int: 225 | return strconv.FormatInt(dataVal.Int(), 10), nil 226 | case reflect.Slice: 227 | dataType := dataVal.Type() 228 | elemKind := dataType.Elem().Kind() 229 | if elemKind == reflect.Uint8 { 230 | return string(dataVal.Interface().([]uint8)), nil 231 | } 232 | case reflect.Uint: 233 | return strconv.FormatUint(dataVal.Uint(), 10), nil 234 | } 235 | } 236 | 237 | return data, nil 238 | } 239 | 240 | func RecursiveStructToMapHookFunc() DecodeHookFunc { 241 | return func(f reflect.Value, t reflect.Value) (interface{}, error) { 242 | if f.Kind() != reflect.Struct { 243 | return f.Interface(), nil 244 | } 245 | 246 | var i interface{} = struct{}{} 247 | if t.Type() != reflect.TypeOf(&i).Elem() { 248 | return f.Interface(), nil 249 | } 250 | 251 | m := make(map[string]interface{}) 252 | t.Set(reflect.ValueOf(m)) 253 | 254 | return f.Interface(), nil 255 | } 256 | } 257 | 258 | // TextUnmarshallerHookFunc returns a DecodeHookFunc that applies 259 | // strings to the UnmarshalText function, when the target type 260 | // implements the encoding.TextUnmarshaler interface 261 | func TextUnmarshallerHookFunc() DecodeHookFuncType { 262 | return func( 263 | f reflect.Type, 264 | t reflect.Type, 265 | data interface{}) (interface{}, error) { 266 | if f.Kind() != reflect.String { 267 | return data, nil 268 | } 269 | result := reflect.New(t).Interface() 270 | unmarshaller, ok := result.(encoding.TextUnmarshaler) 271 | if !ok { 272 | return data, nil 273 | } 274 | str, ok := data.(string) 275 | if !ok { 276 | str = reflect.Indirect(reflect.ValueOf(&data)).Elem().String() 277 | } 278 | if err := unmarshaller.UnmarshalText([]byte(str)); err != nil { 279 | return nil, err 280 | } 281 | return result, nil 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /decode_hooks_test.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "math/big" 7 | "net" 8 | "reflect" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | func TestComposeDecodeHookFunc(t *testing.T) { 14 | f1 := func( 15 | f reflect.Kind, 16 | t reflect.Kind, 17 | data interface{}) (interface{}, error) { 18 | return data.(string) + "foo", nil 19 | } 20 | 21 | f2 := func( 22 | f reflect.Kind, 23 | t reflect.Kind, 24 | data interface{}) (interface{}, error) { 25 | return data.(string) + "bar", nil 26 | } 27 | 28 | f := ComposeDecodeHookFunc(f1, f2) 29 | 30 | result, err := DecodeHookExec( 31 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 32 | if err != nil { 33 | t.Fatalf("bad: %s", err) 34 | } 35 | if result.(string) != "foobar" { 36 | t.Fatalf("bad: %#v", result) 37 | } 38 | } 39 | 40 | func TestComposeDecodeHookFunc_err(t *testing.T) { 41 | f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 42 | return nil, errors.New("foo") 43 | } 44 | 45 | f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 46 | panic("NOPE") 47 | } 48 | 49 | f := ComposeDecodeHookFunc(f1, f2) 50 | 51 | _, err := DecodeHookExec( 52 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 53 | if err.Error() != "foo" { 54 | t.Fatalf("bad: %s", err) 55 | } 56 | } 57 | 58 | func TestComposeDecodeHookFunc_kinds(t *testing.T) { 59 | var f2From reflect.Kind 60 | 61 | f1 := func( 62 | f reflect.Kind, 63 | t reflect.Kind, 64 | data interface{}) (interface{}, error) { 65 | return int(42), nil 66 | } 67 | 68 | f2 := func( 69 | f reflect.Kind, 70 | t reflect.Kind, 71 | data interface{}) (interface{}, error) { 72 | f2From = f 73 | return data, nil 74 | } 75 | 76 | f := ComposeDecodeHookFunc(f1, f2) 77 | 78 | _, err := DecodeHookExec( 79 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 80 | if err != nil { 81 | t.Fatalf("bad: %s", err) 82 | } 83 | if f2From != reflect.Int { 84 | t.Fatalf("bad: %#v", f2From) 85 | } 86 | } 87 | 88 | func TestOrComposeDecodeHookFunc(t *testing.T) { 89 | f1 := func( 90 | f reflect.Kind, 91 | t reflect.Kind, 92 | data interface{}) (interface{}, error) { 93 | return data.(string) + "foo", nil 94 | } 95 | 96 | f2 := func( 97 | f reflect.Kind, 98 | t reflect.Kind, 99 | data interface{}) (interface{}, error) { 100 | return data.(string) + "bar", nil 101 | } 102 | 103 | f := OrComposeDecodeHookFunc(f1, f2) 104 | 105 | result, err := DecodeHookExec( 106 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 107 | if err != nil { 108 | t.Fatalf("bad: %s", err) 109 | } 110 | if result.(string) != "foo" { 111 | t.Fatalf("bad: %#v", result) 112 | } 113 | } 114 | 115 | func TestOrComposeDecodeHookFunc_correctValueIsLast(t *testing.T) { 116 | f1 := func( 117 | f reflect.Kind, 118 | t reflect.Kind, 119 | data interface{}) (interface{}, error) { 120 | return nil, errors.New("f1 error") 121 | } 122 | 123 | f2 := func( 124 | f reflect.Kind, 125 | t reflect.Kind, 126 | data interface{}) (interface{}, error) { 127 | return nil, errors.New("f2 error") 128 | } 129 | 130 | f3 := func( 131 | f reflect.Kind, 132 | t reflect.Kind, 133 | data interface{}) (interface{}, error) { 134 | return data.(string) + "bar", nil 135 | } 136 | 137 | f := OrComposeDecodeHookFunc(f1, f2, f3) 138 | 139 | result, err := DecodeHookExec( 140 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 141 | if err != nil { 142 | t.Fatalf("bad: %s", err) 143 | } 144 | if result.(string) != "bar" { 145 | t.Fatalf("bad: %#v", result) 146 | } 147 | } 148 | 149 | func TestOrComposeDecodeHookFunc_err(t *testing.T) { 150 | f1 := func( 151 | f reflect.Kind, 152 | t reflect.Kind, 153 | data interface{}) (interface{}, error) { 154 | return nil, errors.New("f1 error") 155 | } 156 | 157 | f2 := func( 158 | f reflect.Kind, 159 | t reflect.Kind, 160 | data interface{}) (interface{}, error) { 161 | return nil, errors.New("f2 error") 162 | } 163 | 164 | f := OrComposeDecodeHookFunc(f1, f2) 165 | 166 | _, err := DecodeHookExec( 167 | f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 168 | if err == nil { 169 | t.Fatalf("bad: should return an error") 170 | } 171 | if err.Error() != "f1 error\nf2 error\n" { 172 | t.Fatalf("bad: %s", err) 173 | } 174 | } 175 | 176 | func TestComposeDecodeHookFunc_safe_nofuncs(t *testing.T) { 177 | f := ComposeDecodeHookFunc() 178 | type myStruct2 struct { 179 | MyInt int 180 | } 181 | 182 | type myStruct1 struct { 183 | Blah map[string]myStruct2 184 | } 185 | 186 | src := &myStruct1{Blah: map[string]myStruct2{ 187 | "test": { 188 | MyInt: 1, 189 | }, 190 | }} 191 | 192 | dst := &myStruct1{} 193 | dConf := &DecoderConfig{ 194 | Result: dst, 195 | ErrorUnused: true, 196 | DecodeHook: f, 197 | } 198 | d, err := NewDecoder(dConf) 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | err = d.Decode(src) 203 | if err != nil { 204 | t.Fatal(err) 205 | } 206 | } 207 | 208 | func TestStringToSliceHookFunc(t *testing.T) { 209 | f := StringToSliceHookFunc(",") 210 | 211 | strValue := reflect.ValueOf("42") 212 | sliceValue := reflect.ValueOf([]byte("42")) 213 | cases := []struct { 214 | f, t reflect.Value 215 | result interface{} 216 | err bool 217 | }{ 218 | {sliceValue, sliceValue, []byte("42"), false}, 219 | {strValue, strValue, "42", false}, 220 | { 221 | reflect.ValueOf("foo,bar,baz"), 222 | sliceValue, 223 | []string{"foo", "bar", "baz"}, 224 | false, 225 | }, 226 | { 227 | reflect.ValueOf(""), 228 | sliceValue, 229 | []string{}, 230 | false, 231 | }, 232 | } 233 | 234 | for i, tc := range cases { 235 | actual, err := DecodeHookExec(f, tc.f, tc.t) 236 | if tc.err != (err != nil) { 237 | t.Fatalf("case %d: expected err %#v", i, tc.err) 238 | } 239 | if !reflect.DeepEqual(actual, tc.result) { 240 | t.Fatalf( 241 | "case %d: expected %#v, got %#v", 242 | i, tc.result, actual) 243 | } 244 | } 245 | } 246 | 247 | func TestStringToTimeDurationHookFunc(t *testing.T) { 248 | f := StringToTimeDurationHookFunc() 249 | 250 | timeValue := reflect.ValueOf(time.Duration(5)) 251 | strValue := reflect.ValueOf("") 252 | cases := []struct { 253 | f, t reflect.Value 254 | result interface{} 255 | err bool 256 | }{ 257 | {reflect.ValueOf("5s"), timeValue, 5 * time.Second, false}, 258 | {reflect.ValueOf("5"), timeValue, time.Duration(0), true}, 259 | {reflect.ValueOf("5"), strValue, "5", false}, 260 | } 261 | 262 | for i, tc := range cases { 263 | actual, err := DecodeHookExec(f, tc.f, tc.t) 264 | if tc.err != (err != nil) { 265 | t.Fatalf("case %d: expected err %#v", i, tc.err) 266 | } 267 | if !reflect.DeepEqual(actual, tc.result) { 268 | t.Fatalf( 269 | "case %d: expected %#v, got %#v", 270 | i, tc.result, actual) 271 | } 272 | } 273 | } 274 | 275 | func TestStringToTimeHookFunc(t *testing.T) { 276 | strValue := reflect.ValueOf("5") 277 | timeValue := reflect.ValueOf(time.Time{}) 278 | cases := []struct { 279 | f, t reflect.Value 280 | layout string 281 | result interface{} 282 | err bool 283 | }{ 284 | {reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339, 285 | time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false}, 286 | {strValue, timeValue, time.RFC3339, time.Time{}, true}, 287 | {strValue, strValue, time.RFC3339, "5", false}, 288 | } 289 | 290 | for i, tc := range cases { 291 | f := StringToTimeHookFunc(tc.layout) 292 | actual, err := DecodeHookExec(f, tc.f, tc.t) 293 | if tc.err != (err != nil) { 294 | t.Fatalf("case %d: expected err %#v", i, tc.err) 295 | } 296 | if !reflect.DeepEqual(actual, tc.result) { 297 | t.Fatalf( 298 | "case %d: expected %#v, got %#v", 299 | i, tc.result, actual) 300 | } 301 | } 302 | } 303 | 304 | func TestStringToIPHookFunc(t *testing.T) { 305 | strValue := reflect.ValueOf("5") 306 | ipValue := reflect.ValueOf(net.IP{}) 307 | cases := []struct { 308 | f, t reflect.Value 309 | result interface{} 310 | err bool 311 | }{ 312 | {reflect.ValueOf("1.2.3.4"), ipValue, 313 | net.IPv4(0x01, 0x02, 0x03, 0x04), false}, 314 | {strValue, ipValue, net.IP{}, true}, 315 | {strValue, strValue, "5", false}, 316 | } 317 | 318 | for i, tc := range cases { 319 | f := StringToIPHookFunc() 320 | actual, err := DecodeHookExec(f, tc.f, tc.t) 321 | if tc.err != (err != nil) { 322 | t.Fatalf("case %d: expected err %#v", i, tc.err) 323 | } 324 | if !reflect.DeepEqual(actual, tc.result) { 325 | t.Fatalf( 326 | "case %d: expected %#v, got %#v", 327 | i, tc.result, actual) 328 | } 329 | } 330 | } 331 | 332 | func TestStringToIPNetHookFunc(t *testing.T) { 333 | strValue := reflect.ValueOf("5") 334 | ipNetValue := reflect.ValueOf(net.IPNet{}) 335 | var nilNet *net.IPNet = nil 336 | 337 | cases := []struct { 338 | f, t reflect.Value 339 | result interface{} 340 | err bool 341 | }{ 342 | {reflect.ValueOf("1.2.3.4/24"), ipNetValue, 343 | &net.IPNet{ 344 | IP: net.IP{0x01, 0x02, 0x03, 0x00}, 345 | Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00), 346 | }, false}, 347 | {strValue, ipNetValue, nilNet, true}, 348 | {strValue, strValue, "5", false}, 349 | } 350 | 351 | for i, tc := range cases { 352 | f := StringToIPNetHookFunc() 353 | actual, err := DecodeHookExec(f, tc.f, tc.t) 354 | if tc.err != (err != nil) { 355 | t.Fatalf("case %d: expected err %#v", i, tc.err) 356 | } 357 | if !reflect.DeepEqual(actual, tc.result) { 358 | t.Fatalf( 359 | "case %d: expected %#v, got %#v", 360 | i, tc.result, actual) 361 | } 362 | } 363 | } 364 | 365 | func TestWeaklyTypedHook(t *testing.T) { 366 | var f DecodeHookFunc = WeaklyTypedHook 367 | 368 | strValue := reflect.ValueOf("") 369 | cases := []struct { 370 | f, t reflect.Value 371 | result interface{} 372 | err bool 373 | }{ 374 | // TO STRING 375 | { 376 | reflect.ValueOf(false), 377 | strValue, 378 | "0", 379 | false, 380 | }, 381 | 382 | { 383 | reflect.ValueOf(true), 384 | strValue, 385 | "1", 386 | false, 387 | }, 388 | 389 | { 390 | reflect.ValueOf(float32(7)), 391 | strValue, 392 | "7", 393 | false, 394 | }, 395 | 396 | { 397 | reflect.ValueOf(int(7)), 398 | strValue, 399 | "7", 400 | false, 401 | }, 402 | 403 | { 404 | reflect.ValueOf([]uint8("foo")), 405 | strValue, 406 | "foo", 407 | false, 408 | }, 409 | 410 | { 411 | reflect.ValueOf(uint(7)), 412 | strValue, 413 | "7", 414 | false, 415 | }, 416 | } 417 | 418 | for i, tc := range cases { 419 | actual, err := DecodeHookExec(f, tc.f, tc.t) 420 | if tc.err != (err != nil) { 421 | t.Fatalf("case %d: expected err %#v", i, tc.err) 422 | } 423 | if !reflect.DeepEqual(actual, tc.result) { 424 | t.Fatalf( 425 | "case %d: expected %#v, got %#v", 426 | i, tc.result, actual) 427 | } 428 | } 429 | } 430 | 431 | func TestStructToMapHookFuncTabled(t *testing.T) { 432 | var f DecodeHookFunc = RecursiveStructToMapHookFunc() 433 | 434 | type b struct { 435 | TestKey string 436 | } 437 | 438 | type a struct { 439 | Sub b 440 | } 441 | 442 | testStruct := a{ 443 | Sub: b{ 444 | TestKey: "testval", 445 | }, 446 | } 447 | 448 | testMap := map[string]interface{}{ 449 | "Sub": map[string]interface{}{ 450 | "TestKey": "testval", 451 | }, 452 | } 453 | 454 | cases := []struct { 455 | name string 456 | receiver interface{} 457 | input interface{} 458 | expected interface{} 459 | err bool 460 | }{ 461 | { 462 | "map receiver", 463 | func() interface{} { 464 | var res map[string]interface{} 465 | return &res 466 | }(), 467 | testStruct, 468 | &testMap, 469 | false, 470 | }, 471 | { 472 | "interface receiver", 473 | func() interface{} { 474 | var res interface{} 475 | return &res 476 | }(), 477 | testStruct, 478 | func() interface{} { 479 | var exp interface{} = testMap 480 | return &exp 481 | }(), 482 | false, 483 | }, 484 | { 485 | "slice receiver errors", 486 | func() interface{} { 487 | var res []string 488 | return &res 489 | }(), 490 | testStruct, 491 | new([]string), 492 | true, 493 | }, 494 | { 495 | "slice to slice - no change", 496 | func() interface{} { 497 | var res []string 498 | return &res 499 | }(), 500 | []string{"a", "b"}, 501 | &[]string{"a", "b"}, 502 | false, 503 | }, 504 | { 505 | "string to string - no change", 506 | func() interface{} { 507 | var res string 508 | return &res 509 | }(), 510 | "test", 511 | func() *string { 512 | s := "test" 513 | return &s 514 | }(), 515 | false, 516 | }, 517 | } 518 | 519 | for _, tc := range cases { 520 | t.Run(tc.name, func(t *testing.T) { 521 | cfg := &DecoderConfig{ 522 | DecodeHook: f, 523 | Result: tc.receiver, 524 | } 525 | 526 | d, err := NewDecoder(cfg) 527 | if err != nil { 528 | t.Fatalf("unexpected err %#v", err) 529 | } 530 | 531 | err = d.Decode(tc.input) 532 | if tc.err != (err != nil) { 533 | t.Fatalf("expected err %#v", err) 534 | } 535 | 536 | if !reflect.DeepEqual(tc.expected, tc.receiver) { 537 | t.Fatalf("expected %#v, got %#v", 538 | tc.expected, tc.receiver) 539 | } 540 | }) 541 | 542 | } 543 | } 544 | 545 | func TestTextUnmarshallerHookFunc(t *testing.T) { 546 | type MyString string 547 | 548 | cases := []struct { 549 | f, t reflect.Value 550 | result interface{} 551 | err bool 552 | }{ 553 | {reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false}, 554 | {reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true}, 555 | {reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false}, 556 | {reflect.ValueOf(json.Number("42")), reflect.ValueOf(big.Int{}), big.NewInt(42), false}, 557 | {reflect.ValueOf(MyString("42")), reflect.ValueOf(big.Int{}), big.NewInt(42), false}, 558 | } 559 | for i, tc := range cases { 560 | f := TextUnmarshallerHookFunc() 561 | actual, err := DecodeHookExec(f, tc.f, tc.t) 562 | if tc.err != (err != nil) { 563 | t.Fatalf("case %d: expected err %#v", i, tc.err) 564 | } 565 | if !reflect.DeepEqual(actual, tc.result) { 566 | t.Fatalf( 567 | "case %d: expected %#v, got %#v", 568 | i, tc.result, actual) 569 | } 570 | } 571 | } 572 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // Error implements the error interface and can represents multiple 11 | // errors that occur in the course of a single decode. 12 | type Error struct { 13 | Errors []string 14 | } 15 | 16 | func (e *Error) Error() string { 17 | points := make([]string, len(e.Errors)) 18 | for i, err := range e.Errors { 19 | points[i] = fmt.Sprintf("* %s", err) 20 | } 21 | 22 | sort.Strings(points) 23 | return fmt.Sprintf( 24 | "%d error(s) decoding:\n\n%s", 25 | len(e.Errors), strings.Join(points, "\n")) 26 | } 27 | 28 | // WrappedErrors implements the errwrap.Wrapper interface to make this 29 | // return value more useful with the errwrap and go-multierror libraries. 30 | func (e *Error) WrappedErrors() []error { 31 | if e == nil { 32 | return nil 33 | } 34 | 35 | result := make([]error, len(e.Errors)) 36 | for i, e := range e.Errors { 37 | result[i] = errors.New(e) 38 | } 39 | 40 | return result 41 | } 42 | 43 | func appendErrors(errors []string, err error) []string { 44 | switch e := err.(type) { 45 | case *Error: 46 | return append(errors, e.Errors...) 47 | default: 48 | return append(errors, e.Error()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mitchellh/mapstructure 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /mapstructure.go: -------------------------------------------------------------------------------- 1 | // Package mapstructure exposes functionality to convert one arbitrary 2 | // Go type into another, typically to convert a map[string]interface{} 3 | // into a native Go structure. 4 | // 5 | // The Go structure can be arbitrarily complex, containing slices, 6 | // other structs, etc. and the decoder will properly decode nested 7 | // maps and so on into the proper structures in the native Go struct. 8 | // See the examples to see what the decoder is capable of. 9 | // 10 | // The simplest function to start with is Decode. 11 | // 12 | // Field Tags 13 | // 14 | // When decoding to a struct, mapstructure will use the field name by 15 | // default to perform the mapping. For example, if a struct has a field 16 | // "Username" then mapstructure will look for a key in the source value 17 | // of "username" (case insensitive). 18 | // 19 | // type User struct { 20 | // Username string 21 | // } 22 | // 23 | // You can change the behavior of mapstructure by using struct tags. 24 | // The default struct tag that mapstructure looks for is "mapstructure" 25 | // but you can customize it using DecoderConfig. 26 | // 27 | // Renaming Fields 28 | // 29 | // To rename the key that mapstructure looks for, use the "mapstructure" 30 | // tag and set a value directly. For example, to change the "username" example 31 | // above to "user": 32 | // 33 | // type User struct { 34 | // Username string `mapstructure:"user"` 35 | // } 36 | // 37 | // Embedded Structs and Squashing 38 | // 39 | // Embedded structs are treated as if they're another field with that name. 40 | // By default, the two structs below are equivalent when decoding with 41 | // mapstructure: 42 | // 43 | // type Person struct { 44 | // Name string 45 | // } 46 | // 47 | // type Friend struct { 48 | // Person 49 | // } 50 | // 51 | // type Friend struct { 52 | // Person Person 53 | // } 54 | // 55 | // This would require an input that looks like below: 56 | // 57 | // map[string]interface{}{ 58 | // "person": map[string]interface{}{"name": "alice"}, 59 | // } 60 | // 61 | // If your "person" value is NOT nested, then you can append ",squash" to 62 | // your tag value and mapstructure will treat it as if the embedded struct 63 | // were part of the struct directly. Example: 64 | // 65 | // type Friend struct { 66 | // Person `mapstructure:",squash"` 67 | // } 68 | // 69 | // Now the following input would be accepted: 70 | // 71 | // map[string]interface{}{ 72 | // "name": "alice", 73 | // } 74 | // 75 | // When decoding from a struct to a map, the squash tag squashes the struct 76 | // fields into a single map. Using the example structs from above: 77 | // 78 | // Friend{Person: Person{Name: "alice"}} 79 | // 80 | // Will be decoded into a map: 81 | // 82 | // map[string]interface{}{ 83 | // "name": "alice", 84 | // } 85 | // 86 | // DecoderConfig has a field that changes the behavior of mapstructure 87 | // to always squash embedded structs. 88 | // 89 | // Remainder Values 90 | // 91 | // If there are any unmapped keys in the source value, mapstructure by 92 | // default will silently ignore them. You can error by setting ErrorUnused 93 | // in DecoderConfig. If you're using Metadata you can also maintain a slice 94 | // of the unused keys. 95 | // 96 | // You can also use the ",remain" suffix on your tag to collect all unused 97 | // values in a map. The field with this tag MUST be a map type and should 98 | // probably be a "map[string]interface{}" or "map[interface{}]interface{}". 99 | // See example below: 100 | // 101 | // type Friend struct { 102 | // Name string 103 | // Other map[string]interface{} `mapstructure:",remain"` 104 | // } 105 | // 106 | // Given the input below, Other would be populated with the other 107 | // values that weren't used (everything but "name"): 108 | // 109 | // map[string]interface{}{ 110 | // "name": "bob", 111 | // "address": "123 Maple St.", 112 | // } 113 | // 114 | // Omit Empty Values 115 | // 116 | // When decoding from a struct to any other value, you may use the 117 | // ",omitempty" suffix on your tag to omit that value if it equates to 118 | // the zero value. The zero value of all types is specified in the Go 119 | // specification. 120 | // 121 | // For example, the zero type of a numeric type is zero ("0"). If the struct 122 | // field value is zero and a numeric type, the field is empty, and it won't 123 | // be encoded into the destination type. 124 | // 125 | // type Source struct { 126 | // Age int `mapstructure:",omitempty"` 127 | // } 128 | // 129 | // Unexported fields 130 | // 131 | // Since unexported (private) struct fields cannot be set outside the package 132 | // where they are defined, the decoder will simply skip them. 133 | // 134 | // For this output type definition: 135 | // 136 | // type Exported struct { 137 | // private string // this unexported field will be skipped 138 | // Public string 139 | // } 140 | // 141 | // Using this map as input: 142 | // 143 | // map[string]interface{}{ 144 | // "private": "I will be ignored", 145 | // "Public": "I made it through!", 146 | // } 147 | // 148 | // The following struct will be decoded: 149 | // 150 | // type Exported struct { 151 | // private: "" // field is left with an empty string (zero value) 152 | // Public: "I made it through!" 153 | // } 154 | // 155 | // Other Configuration 156 | // 157 | // mapstructure is highly configurable. See the DecoderConfig struct 158 | // for other features and options that are supported. 159 | package mapstructure 160 | 161 | import ( 162 | "encoding/json" 163 | "errors" 164 | "fmt" 165 | "reflect" 166 | "sort" 167 | "strconv" 168 | "strings" 169 | ) 170 | 171 | // DecodeHookFunc is the callback function that can be used for 172 | // data transformations. See "DecodeHook" in the DecoderConfig 173 | // struct. 174 | // 175 | // The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or 176 | // DecodeHookFuncValue. 177 | // Values are a superset of Types (Values can return types), and Types are a 178 | // superset of Kinds (Types can return Kinds) and are generally a richer thing 179 | // to use, but Kinds are simpler if you only need those. 180 | // 181 | // The reason DecodeHookFunc is multi-typed is for backwards compatibility: 182 | // we started with Kinds and then realized Types were the better solution, 183 | // but have a promise to not break backwards compat so we now support 184 | // both. 185 | type DecodeHookFunc interface{} 186 | 187 | // DecodeHookFuncType is a DecodeHookFunc which has complete information about 188 | // the source and target types. 189 | type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) 190 | 191 | // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the 192 | // source and target types. 193 | type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) 194 | 195 | // DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target 196 | // values. 197 | type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error) 198 | 199 | // DecoderConfig is the configuration that is used to create a new decoder 200 | // and allows customization of various aspects of decoding. 201 | type DecoderConfig struct { 202 | // DecodeHook, if set, will be called before any decoding and any 203 | // type conversion (if WeaklyTypedInput is on). This lets you modify 204 | // the values before they're set down onto the resulting struct. The 205 | // DecodeHook is called for every map and value in the input. This means 206 | // that if a struct has embedded fields with squash tags the decode hook 207 | // is called only once with all of the input data, not once for each 208 | // embedded struct. 209 | // 210 | // If an error is returned, the entire decode will fail with that error. 211 | DecodeHook DecodeHookFunc 212 | 213 | // If ErrorUnused is true, then it is an error for there to exist 214 | // keys in the original map that were unused in the decoding process 215 | // (extra keys). 216 | ErrorUnused bool 217 | 218 | // If ErrorUnset is true, then it is an error for there to exist 219 | // fields in the result that were not set in the decoding process 220 | // (extra fields). This only applies to decoding to a struct. This 221 | // will affect all nested structs as well. 222 | ErrorUnset bool 223 | 224 | // ZeroFields, if set to true, will zero fields before writing them. 225 | // For example, a map will be emptied before decoded values are put in 226 | // it. If this is false, a map will be merged. 227 | ZeroFields bool 228 | 229 | // If WeaklyTypedInput is true, the decoder will make the following 230 | // "weak" conversions: 231 | // 232 | // - bools to string (true = "1", false = "0") 233 | // - numbers to string (base 10) 234 | // - bools to int/uint (true = 1, false = 0) 235 | // - strings to int/uint (base implied by prefix) 236 | // - int to bool (true if value != 0) 237 | // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, 238 | // FALSE, false, False. Anything else is an error) 239 | // - empty array = empty map and vice versa 240 | // - negative numbers to overflowed uint values (base 10) 241 | // - slice of maps to a merged map 242 | // - single values are converted to slices if required. Each 243 | // element is weakly decoded. For example: "4" can become []int{4} 244 | // if the target type is an int slice. 245 | // 246 | WeaklyTypedInput bool 247 | 248 | // Squash will squash embedded structs. A squash tag may also be 249 | // added to an individual struct field using a tag. For example: 250 | // 251 | // type Parent struct { 252 | // Child `mapstructure:",squash"` 253 | // } 254 | Squash bool 255 | 256 | // Metadata is the struct that will contain extra metadata about 257 | // the decoding. If this is nil, then no metadata will be tracked. 258 | Metadata *Metadata 259 | 260 | // Result is a pointer to the struct that will contain the decoded 261 | // value. 262 | Result interface{} 263 | 264 | // The tag name that mapstructure reads for field names. This 265 | // defaults to "mapstructure" 266 | TagName string 267 | 268 | // IgnoreUntaggedFields ignores all struct fields without explicit 269 | // TagName, comparable to `mapstructure:"-"` as default behaviour. 270 | IgnoreUntaggedFields bool 271 | 272 | // MatchName is the function used to match the map key to the struct 273 | // field name or tag. Defaults to `strings.EqualFold`. This can be used 274 | // to implement case-sensitive tag values, support snake casing, etc. 275 | MatchName func(mapKey, fieldName string) bool 276 | } 277 | 278 | // A Decoder takes a raw interface value and turns it into structured 279 | // data, keeping track of rich error information along the way in case 280 | // anything goes wrong. Unlike the basic top-level Decode method, you can 281 | // more finely control how the Decoder behaves using the DecoderConfig 282 | // structure. The top-level Decode method is just a convenience that sets 283 | // up the most basic Decoder. 284 | type Decoder struct { 285 | config *DecoderConfig 286 | } 287 | 288 | // Metadata contains information about decoding a structure that 289 | // is tedious or difficult to get otherwise. 290 | type Metadata struct { 291 | // Keys are the keys of the structure which were successfully decoded 292 | Keys []string 293 | 294 | // Unused is a slice of keys that were found in the raw value but 295 | // weren't decoded since there was no matching field in the result interface 296 | Unused []string 297 | 298 | // Unset is a slice of field names that were found in the result interface 299 | // but weren't set in the decoding process since there was no matching value 300 | // in the input 301 | Unset []string 302 | } 303 | 304 | // Decode takes an input structure and uses reflection to translate it to 305 | // the output structure. output must be a pointer to a map or struct. 306 | func Decode(input interface{}, output interface{}) error { 307 | config := &DecoderConfig{ 308 | Metadata: nil, 309 | Result: output, 310 | } 311 | 312 | decoder, err := NewDecoder(config) 313 | if err != nil { 314 | return err 315 | } 316 | 317 | return decoder.Decode(input) 318 | } 319 | 320 | // WeakDecode is the same as Decode but is shorthand to enable 321 | // WeaklyTypedInput. See DecoderConfig for more info. 322 | func WeakDecode(input, output interface{}) error { 323 | config := &DecoderConfig{ 324 | Metadata: nil, 325 | Result: output, 326 | WeaklyTypedInput: true, 327 | } 328 | 329 | decoder, err := NewDecoder(config) 330 | if err != nil { 331 | return err 332 | } 333 | 334 | return decoder.Decode(input) 335 | } 336 | 337 | // DecodeMetadata is the same as Decode, but is shorthand to 338 | // enable metadata collection. See DecoderConfig for more info. 339 | func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { 340 | config := &DecoderConfig{ 341 | Metadata: metadata, 342 | Result: output, 343 | } 344 | 345 | decoder, err := NewDecoder(config) 346 | if err != nil { 347 | return err 348 | } 349 | 350 | return decoder.Decode(input) 351 | } 352 | 353 | // WeakDecodeMetadata is the same as Decode, but is shorthand to 354 | // enable both WeaklyTypedInput and metadata collection. See 355 | // DecoderConfig for more info. 356 | func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { 357 | config := &DecoderConfig{ 358 | Metadata: metadata, 359 | Result: output, 360 | WeaklyTypedInput: true, 361 | } 362 | 363 | decoder, err := NewDecoder(config) 364 | if err != nil { 365 | return err 366 | } 367 | 368 | return decoder.Decode(input) 369 | } 370 | 371 | // NewDecoder returns a new decoder for the given configuration. Once 372 | // a decoder has been returned, the same configuration must not be used 373 | // again. 374 | func NewDecoder(config *DecoderConfig) (*Decoder, error) { 375 | val := reflect.ValueOf(config.Result) 376 | if val.Kind() != reflect.Ptr { 377 | return nil, errors.New("result must be a pointer") 378 | } 379 | 380 | val = val.Elem() 381 | if !val.CanAddr() { 382 | return nil, errors.New("result must be addressable (a pointer)") 383 | } 384 | 385 | if config.Metadata != nil { 386 | if config.Metadata.Keys == nil { 387 | config.Metadata.Keys = make([]string, 0) 388 | } 389 | 390 | if config.Metadata.Unused == nil { 391 | config.Metadata.Unused = make([]string, 0) 392 | } 393 | 394 | if config.Metadata.Unset == nil { 395 | config.Metadata.Unset = make([]string, 0) 396 | } 397 | } 398 | 399 | if config.TagName == "" { 400 | config.TagName = "mapstructure" 401 | } 402 | 403 | if config.MatchName == nil { 404 | config.MatchName = strings.EqualFold 405 | } 406 | 407 | result := &Decoder{ 408 | config: config, 409 | } 410 | 411 | return result, nil 412 | } 413 | 414 | // Decode decodes the given raw interface to the target pointer specified 415 | // by the configuration. 416 | func (d *Decoder) Decode(input interface{}) error { 417 | return d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) 418 | } 419 | 420 | // Decodes an unknown data type into a specific reflection value. 421 | func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { 422 | var inputVal reflect.Value 423 | if input != nil { 424 | inputVal = reflect.ValueOf(input) 425 | 426 | // We need to check here if input is a typed nil. Typed nils won't 427 | // match the "input == nil" below so we check that here. 428 | if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { 429 | input = nil 430 | } 431 | } 432 | 433 | if input == nil { 434 | // If the data is nil, then we don't set anything, unless ZeroFields is set 435 | // to true. 436 | if d.config.ZeroFields { 437 | outVal.Set(reflect.Zero(outVal.Type())) 438 | 439 | if d.config.Metadata != nil && name != "" { 440 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) 441 | } 442 | } 443 | return nil 444 | } 445 | 446 | if !inputVal.IsValid() { 447 | // If the input value is invalid, then we just set the value 448 | // to be the zero value. 449 | outVal.Set(reflect.Zero(outVal.Type())) 450 | if d.config.Metadata != nil && name != "" { 451 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) 452 | } 453 | return nil 454 | } 455 | 456 | if d.config.DecodeHook != nil { 457 | // We have a DecodeHook, so let's pre-process the input. 458 | var err error 459 | input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal) 460 | if err != nil { 461 | return fmt.Errorf("error decoding '%s': %w", name, err) 462 | } 463 | } 464 | 465 | var err error 466 | outputKind := getKind(outVal) 467 | addMetaKey := true 468 | switch outputKind { 469 | case reflect.Bool: 470 | err = d.decodeBool(name, input, outVal) 471 | case reflect.Interface: 472 | err = d.decodeBasic(name, input, outVal) 473 | case reflect.String: 474 | err = d.decodeString(name, input, outVal) 475 | case reflect.Int: 476 | err = d.decodeInt(name, input, outVal) 477 | case reflect.Uint: 478 | err = d.decodeUint(name, input, outVal) 479 | case reflect.Float32: 480 | err = d.decodeFloat(name, input, outVal) 481 | case reflect.Struct: 482 | err = d.decodeStruct(name, input, outVal) 483 | case reflect.Map: 484 | err = d.decodeMap(name, input, outVal) 485 | case reflect.Ptr: 486 | addMetaKey, err = d.decodePtr(name, input, outVal) 487 | case reflect.Slice: 488 | err = d.decodeSlice(name, input, outVal) 489 | case reflect.Array: 490 | err = d.decodeArray(name, input, outVal) 491 | case reflect.Func: 492 | err = d.decodeFunc(name, input, outVal) 493 | default: 494 | // If we reached this point then we weren't able to decode it 495 | return fmt.Errorf("%s: unsupported type: %s", name, outputKind) 496 | } 497 | 498 | // If we reached here, then we successfully decoded SOMETHING, so 499 | // mark the key as used if we're tracking metainput. 500 | if addMetaKey && d.config.Metadata != nil && name != "" { 501 | d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) 502 | } 503 | 504 | return err 505 | } 506 | 507 | // This decodes a basic type (bool, int, string, etc.) and sets the 508 | // value to "data" of that type. 509 | func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { 510 | if val.IsValid() && val.Elem().IsValid() { 511 | elem := val.Elem() 512 | 513 | // If we can't address this element, then its not writable. Instead, 514 | // we make a copy of the value (which is a pointer and therefore 515 | // writable), decode into that, and replace the whole value. 516 | copied := false 517 | if !elem.CanAddr() { 518 | copied = true 519 | 520 | // Make *T 521 | copy := reflect.New(elem.Type()) 522 | 523 | // *T = elem 524 | copy.Elem().Set(elem) 525 | 526 | // Set elem so we decode into it 527 | elem = copy 528 | } 529 | 530 | // Decode. If we have an error then return. We also return right 531 | // away if we're not a copy because that means we decoded directly. 532 | if err := d.decode(name, data, elem); err != nil || !copied { 533 | return err 534 | } 535 | 536 | // If we're a copy, we need to set te final result 537 | val.Set(elem.Elem()) 538 | return nil 539 | } 540 | 541 | dataVal := reflect.ValueOf(data) 542 | 543 | // If the input data is a pointer, and the assigned type is the dereference 544 | // of that exact pointer, then indirect it so that we can assign it. 545 | // Example: *string to string 546 | if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { 547 | dataVal = reflect.Indirect(dataVal) 548 | } 549 | 550 | if !dataVal.IsValid() { 551 | dataVal = reflect.Zero(val.Type()) 552 | } 553 | 554 | dataValType := dataVal.Type() 555 | if !dataValType.AssignableTo(val.Type()) { 556 | return fmt.Errorf( 557 | "'%s' expected type '%s', got '%s'", 558 | name, val.Type(), dataValType) 559 | } 560 | 561 | val.Set(dataVal) 562 | return nil 563 | } 564 | 565 | func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { 566 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 567 | dataKind := getKind(dataVal) 568 | 569 | converted := true 570 | switch { 571 | case dataKind == reflect.String: 572 | val.SetString(dataVal.String()) 573 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: 574 | if dataVal.Bool() { 575 | val.SetString("1") 576 | } else { 577 | val.SetString("0") 578 | } 579 | case dataKind == reflect.Int && d.config.WeaklyTypedInput: 580 | val.SetString(strconv.FormatInt(dataVal.Int(), 10)) 581 | case dataKind == reflect.Uint && d.config.WeaklyTypedInput: 582 | val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) 583 | case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: 584 | val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) 585 | case dataKind == reflect.Slice && d.config.WeaklyTypedInput, 586 | dataKind == reflect.Array && d.config.WeaklyTypedInput: 587 | dataType := dataVal.Type() 588 | elemKind := dataType.Elem().Kind() 589 | switch elemKind { 590 | case reflect.Uint8: 591 | var uints []uint8 592 | if dataKind == reflect.Array { 593 | uints = make([]uint8, dataVal.Len(), dataVal.Len()) 594 | for i := range uints { 595 | uints[i] = dataVal.Index(i).Interface().(uint8) 596 | } 597 | } else { 598 | uints = dataVal.Interface().([]uint8) 599 | } 600 | val.SetString(string(uints)) 601 | default: 602 | converted = false 603 | } 604 | default: 605 | converted = false 606 | } 607 | 608 | if !converted { 609 | return fmt.Errorf( 610 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 611 | name, val.Type(), dataVal.Type(), data) 612 | } 613 | 614 | return nil 615 | } 616 | 617 | func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { 618 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 619 | dataKind := getKind(dataVal) 620 | dataType := dataVal.Type() 621 | 622 | switch { 623 | case dataKind == reflect.Int: 624 | val.SetInt(dataVal.Int()) 625 | case dataKind == reflect.Uint: 626 | val.SetInt(int64(dataVal.Uint())) 627 | case dataKind == reflect.Float32: 628 | val.SetInt(int64(dataVal.Float())) 629 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: 630 | if dataVal.Bool() { 631 | val.SetInt(1) 632 | } else { 633 | val.SetInt(0) 634 | } 635 | case dataKind == reflect.String && d.config.WeaklyTypedInput: 636 | str := dataVal.String() 637 | if str == "" { 638 | str = "0" 639 | } 640 | 641 | i, err := strconv.ParseInt(str, 0, val.Type().Bits()) 642 | if err == nil { 643 | val.SetInt(i) 644 | } else { 645 | return fmt.Errorf("cannot parse '%s' as int: %s", name, err) 646 | } 647 | case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": 648 | jn := data.(json.Number) 649 | i, err := jn.Int64() 650 | if err != nil { 651 | return fmt.Errorf( 652 | "error decoding json.Number into %s: %s", name, err) 653 | } 654 | val.SetInt(i) 655 | default: 656 | return fmt.Errorf( 657 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 658 | name, val.Type(), dataVal.Type(), data) 659 | } 660 | 661 | return nil 662 | } 663 | 664 | func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { 665 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 666 | dataKind := getKind(dataVal) 667 | dataType := dataVal.Type() 668 | 669 | switch { 670 | case dataKind == reflect.Int: 671 | i := dataVal.Int() 672 | if i < 0 && !d.config.WeaklyTypedInput { 673 | return fmt.Errorf("cannot parse '%s', %d overflows uint", 674 | name, i) 675 | } 676 | val.SetUint(uint64(i)) 677 | case dataKind == reflect.Uint: 678 | val.SetUint(dataVal.Uint()) 679 | case dataKind == reflect.Float32: 680 | f := dataVal.Float() 681 | if f < 0 && !d.config.WeaklyTypedInput { 682 | return fmt.Errorf("cannot parse '%s', %f overflows uint", 683 | name, f) 684 | } 685 | val.SetUint(uint64(f)) 686 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: 687 | if dataVal.Bool() { 688 | val.SetUint(1) 689 | } else { 690 | val.SetUint(0) 691 | } 692 | case dataKind == reflect.String && d.config.WeaklyTypedInput: 693 | str := dataVal.String() 694 | if str == "" { 695 | str = "0" 696 | } 697 | 698 | i, err := strconv.ParseUint(str, 0, val.Type().Bits()) 699 | if err == nil { 700 | val.SetUint(i) 701 | } else { 702 | return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) 703 | } 704 | case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": 705 | jn := data.(json.Number) 706 | i, err := strconv.ParseUint(string(jn), 0, 64) 707 | if err != nil { 708 | return fmt.Errorf( 709 | "error decoding json.Number into %s: %s", name, err) 710 | } 711 | val.SetUint(i) 712 | default: 713 | return fmt.Errorf( 714 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 715 | name, val.Type(), dataVal.Type(), data) 716 | } 717 | 718 | return nil 719 | } 720 | 721 | func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { 722 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 723 | dataKind := getKind(dataVal) 724 | 725 | switch { 726 | case dataKind == reflect.Bool: 727 | val.SetBool(dataVal.Bool()) 728 | case dataKind == reflect.Int && d.config.WeaklyTypedInput: 729 | val.SetBool(dataVal.Int() != 0) 730 | case dataKind == reflect.Uint && d.config.WeaklyTypedInput: 731 | val.SetBool(dataVal.Uint() != 0) 732 | case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: 733 | val.SetBool(dataVal.Float() != 0) 734 | case dataKind == reflect.String && d.config.WeaklyTypedInput: 735 | b, err := strconv.ParseBool(dataVal.String()) 736 | if err == nil { 737 | val.SetBool(b) 738 | } else if dataVal.String() == "" { 739 | val.SetBool(false) 740 | } else { 741 | return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) 742 | } 743 | default: 744 | return fmt.Errorf( 745 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 746 | name, val.Type(), dataVal.Type(), data) 747 | } 748 | 749 | return nil 750 | } 751 | 752 | func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { 753 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 754 | dataKind := getKind(dataVal) 755 | dataType := dataVal.Type() 756 | 757 | switch { 758 | case dataKind == reflect.Int: 759 | val.SetFloat(float64(dataVal.Int())) 760 | case dataKind == reflect.Uint: 761 | val.SetFloat(float64(dataVal.Uint())) 762 | case dataKind == reflect.Float32: 763 | val.SetFloat(dataVal.Float()) 764 | case dataKind == reflect.Bool && d.config.WeaklyTypedInput: 765 | if dataVal.Bool() { 766 | val.SetFloat(1) 767 | } else { 768 | val.SetFloat(0) 769 | } 770 | case dataKind == reflect.String && d.config.WeaklyTypedInput: 771 | str := dataVal.String() 772 | if str == "" { 773 | str = "0" 774 | } 775 | 776 | f, err := strconv.ParseFloat(str, val.Type().Bits()) 777 | if err == nil { 778 | val.SetFloat(f) 779 | } else { 780 | return fmt.Errorf("cannot parse '%s' as float: %s", name, err) 781 | } 782 | case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": 783 | jn := data.(json.Number) 784 | i, err := jn.Float64() 785 | if err != nil { 786 | return fmt.Errorf( 787 | "error decoding json.Number into %s: %s", name, err) 788 | } 789 | val.SetFloat(i) 790 | default: 791 | return fmt.Errorf( 792 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 793 | name, val.Type(), dataVal.Type(), data) 794 | } 795 | 796 | return nil 797 | } 798 | 799 | func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { 800 | valType := val.Type() 801 | valKeyType := valType.Key() 802 | valElemType := valType.Elem() 803 | 804 | // By default we overwrite keys in the current map 805 | valMap := val 806 | 807 | // If the map is nil or we're purposely zeroing fields, make a new map 808 | if valMap.IsNil() || d.config.ZeroFields { 809 | // Make a new map to hold our result 810 | mapType := reflect.MapOf(valKeyType, valElemType) 811 | valMap = reflect.MakeMap(mapType) 812 | } 813 | 814 | // Check input type and based on the input type jump to the proper func 815 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 816 | switch dataVal.Kind() { 817 | case reflect.Map: 818 | return d.decodeMapFromMap(name, dataVal, val, valMap) 819 | 820 | case reflect.Struct: 821 | return d.decodeMapFromStruct(name, dataVal, val, valMap) 822 | 823 | case reflect.Array, reflect.Slice: 824 | if d.config.WeaklyTypedInput { 825 | return d.decodeMapFromSlice(name, dataVal, val, valMap) 826 | } 827 | 828 | fallthrough 829 | 830 | default: 831 | return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) 832 | } 833 | } 834 | 835 | func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { 836 | // Special case for BC reasons (covered by tests) 837 | if dataVal.Len() == 0 { 838 | val.Set(valMap) 839 | return nil 840 | } 841 | 842 | for i := 0; i < dataVal.Len(); i++ { 843 | err := d.decode( 844 | name+"["+strconv.Itoa(i)+"]", 845 | dataVal.Index(i).Interface(), val) 846 | if err != nil { 847 | return err 848 | } 849 | } 850 | 851 | return nil 852 | } 853 | 854 | func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { 855 | valType := val.Type() 856 | valKeyType := valType.Key() 857 | valElemType := valType.Elem() 858 | 859 | // Accumulate errors 860 | errors := make([]string, 0) 861 | 862 | // If the input data is empty, then we just match what the input data is. 863 | if dataVal.Len() == 0 { 864 | if dataVal.IsNil() { 865 | if !val.IsNil() { 866 | val.Set(dataVal) 867 | } 868 | } else { 869 | // Set to empty allocated value 870 | val.Set(valMap) 871 | } 872 | 873 | return nil 874 | } 875 | 876 | for _, k := range dataVal.MapKeys() { 877 | fieldName := name + "[" + k.String() + "]" 878 | 879 | // First decode the key into the proper type 880 | currentKey := reflect.Indirect(reflect.New(valKeyType)) 881 | if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { 882 | errors = appendErrors(errors, err) 883 | continue 884 | } 885 | 886 | // Next decode the data into the proper type 887 | v := dataVal.MapIndex(k).Interface() 888 | currentVal := reflect.Indirect(reflect.New(valElemType)) 889 | if err := d.decode(fieldName, v, currentVal); err != nil { 890 | errors = appendErrors(errors, err) 891 | continue 892 | } 893 | 894 | valMap.SetMapIndex(currentKey, currentVal) 895 | } 896 | 897 | // Set the built up map to the value 898 | val.Set(valMap) 899 | 900 | // If we had errors, return those 901 | if len(errors) > 0 { 902 | return &Error{errors} 903 | } 904 | 905 | return nil 906 | } 907 | 908 | func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { 909 | typ := dataVal.Type() 910 | for i := 0; i < typ.NumField(); i++ { 911 | // Get the StructField first since this is a cheap operation. If the 912 | // field is unexported, then ignore it. 913 | f := typ.Field(i) 914 | if f.PkgPath != "" { 915 | continue 916 | } 917 | 918 | // Next get the actual value of this field and verify it is assignable 919 | // to the map value. 920 | v := dataVal.Field(i) 921 | if !v.Type().AssignableTo(valMap.Type().Elem()) { 922 | return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) 923 | } 924 | 925 | tagValue := f.Tag.Get(d.config.TagName) 926 | keyName := f.Name 927 | 928 | if tagValue == "" && d.config.IgnoreUntaggedFields { 929 | continue 930 | } 931 | 932 | // If Squash is set in the config, we squash the field down. 933 | squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous 934 | 935 | v = dereferencePtrToStructIfNeeded(v, d.config.TagName) 936 | 937 | // Determine the name of the key in the map 938 | if index := strings.Index(tagValue, ","); index != -1 { 939 | if tagValue[:index] == "-" { 940 | continue 941 | } 942 | // If "omitempty" is specified in the tag, it ignores empty values. 943 | if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { 944 | continue 945 | } 946 | 947 | // If "squash" is specified in the tag, we squash the field down. 948 | squash = squash || strings.Index(tagValue[index+1:], "squash") != -1 949 | if squash { 950 | // When squashing, the embedded type can be a pointer to a struct. 951 | if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { 952 | v = v.Elem() 953 | } 954 | 955 | // The final type must be a struct 956 | if v.Kind() != reflect.Struct { 957 | return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) 958 | } 959 | } 960 | if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { 961 | keyName = keyNameTagValue 962 | } 963 | } else if len(tagValue) > 0 { 964 | if tagValue == "-" { 965 | continue 966 | } 967 | keyName = tagValue 968 | } 969 | 970 | switch v.Kind() { 971 | // this is an embedded struct, so handle it differently 972 | case reflect.Struct: 973 | x := reflect.New(v.Type()) 974 | x.Elem().Set(v) 975 | 976 | vType := valMap.Type() 977 | vKeyType := vType.Key() 978 | vElemType := vType.Elem() 979 | mType := reflect.MapOf(vKeyType, vElemType) 980 | vMap := reflect.MakeMap(mType) 981 | 982 | // Creating a pointer to a map so that other methods can completely 983 | // overwrite the map if need be (looking at you decodeMapFromMap). The 984 | // indirection allows the underlying map to be settable (CanSet() == true) 985 | // where as reflect.MakeMap returns an unsettable map. 986 | addrVal := reflect.New(vMap.Type()) 987 | reflect.Indirect(addrVal).Set(vMap) 988 | 989 | err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) 990 | if err != nil { 991 | return err 992 | } 993 | 994 | // the underlying map may have been completely overwritten so pull 995 | // it indirectly out of the enclosing value. 996 | vMap = reflect.Indirect(addrVal) 997 | 998 | if squash { 999 | for _, k := range vMap.MapKeys() { 1000 | valMap.SetMapIndex(k, vMap.MapIndex(k)) 1001 | } 1002 | } else { 1003 | valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) 1004 | } 1005 | 1006 | default: 1007 | valMap.SetMapIndex(reflect.ValueOf(keyName), v) 1008 | } 1009 | } 1010 | 1011 | if val.CanAddr() { 1012 | val.Set(valMap) 1013 | } 1014 | 1015 | return nil 1016 | } 1017 | 1018 | func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) { 1019 | // If the input data is nil, then we want to just set the output 1020 | // pointer to be nil as well. 1021 | isNil := data == nil 1022 | if !isNil { 1023 | switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { 1024 | case reflect.Chan, 1025 | reflect.Func, 1026 | reflect.Interface, 1027 | reflect.Map, 1028 | reflect.Ptr, 1029 | reflect.Slice: 1030 | isNil = v.IsNil() 1031 | } 1032 | } 1033 | if isNil { 1034 | if !val.IsNil() && val.CanSet() { 1035 | nilValue := reflect.New(val.Type()).Elem() 1036 | val.Set(nilValue) 1037 | } 1038 | 1039 | return true, nil 1040 | } 1041 | 1042 | // Create an element of the concrete (non pointer) type and decode 1043 | // into that. Then set the value of the pointer to this type. 1044 | valType := val.Type() 1045 | valElemType := valType.Elem() 1046 | if val.CanSet() { 1047 | realVal := val 1048 | if realVal.IsNil() || d.config.ZeroFields { 1049 | realVal = reflect.New(valElemType) 1050 | } 1051 | 1052 | if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { 1053 | return false, err 1054 | } 1055 | 1056 | val.Set(realVal) 1057 | } else { 1058 | if err := d.decode(name, data, reflect.Indirect(val)); err != nil { 1059 | return false, err 1060 | } 1061 | } 1062 | return false, nil 1063 | } 1064 | 1065 | func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { 1066 | // Create an element of the concrete (non pointer) type and decode 1067 | // into that. Then set the value of the pointer to this type. 1068 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 1069 | if val.Type() != dataVal.Type() { 1070 | return fmt.Errorf( 1071 | "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", 1072 | name, val.Type(), dataVal.Type(), data) 1073 | } 1074 | val.Set(dataVal) 1075 | return nil 1076 | } 1077 | 1078 | func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { 1079 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 1080 | dataValKind := dataVal.Kind() 1081 | valType := val.Type() 1082 | valElemType := valType.Elem() 1083 | sliceType := reflect.SliceOf(valElemType) 1084 | 1085 | // If we have a non array/slice type then we first attempt to convert. 1086 | if dataValKind != reflect.Array && dataValKind != reflect.Slice { 1087 | if d.config.WeaklyTypedInput { 1088 | switch { 1089 | // Slice and array we use the normal logic 1090 | case dataValKind == reflect.Slice, dataValKind == reflect.Array: 1091 | break 1092 | 1093 | // Empty maps turn into empty slices 1094 | case dataValKind == reflect.Map: 1095 | if dataVal.Len() == 0 { 1096 | val.Set(reflect.MakeSlice(sliceType, 0, 0)) 1097 | return nil 1098 | } 1099 | // Create slice of maps of other sizes 1100 | return d.decodeSlice(name, []interface{}{data}, val) 1101 | 1102 | case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: 1103 | return d.decodeSlice(name, []byte(dataVal.String()), val) 1104 | 1105 | // All other types we try to convert to the slice type 1106 | // and "lift" it into it. i.e. a string becomes a string slice. 1107 | default: 1108 | // Just re-try this function with data as a slice. 1109 | return d.decodeSlice(name, []interface{}{data}, val) 1110 | } 1111 | } 1112 | 1113 | return fmt.Errorf( 1114 | "'%s': source data must be an array or slice, got %s", name, dataValKind) 1115 | } 1116 | 1117 | // If the input value is nil, then don't allocate since empty != nil 1118 | if dataValKind != reflect.Array && dataVal.IsNil() { 1119 | return nil 1120 | } 1121 | 1122 | valSlice := val 1123 | if valSlice.IsNil() || d.config.ZeroFields { 1124 | // Make a new slice to hold our result, same size as the original data. 1125 | valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) 1126 | } else if valSlice.Len() > dataVal.Len() { 1127 | valSlice = valSlice.Slice(0, dataVal.Len()) 1128 | } 1129 | 1130 | // Accumulate any errors 1131 | errors := make([]string, 0) 1132 | 1133 | for i := 0; i < dataVal.Len(); i++ { 1134 | currentData := dataVal.Index(i).Interface() 1135 | for valSlice.Len() <= i { 1136 | valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) 1137 | } 1138 | currentField := valSlice.Index(i) 1139 | 1140 | fieldName := name + "[" + strconv.Itoa(i) + "]" 1141 | if err := d.decode(fieldName, currentData, currentField); err != nil { 1142 | errors = appendErrors(errors, err) 1143 | } 1144 | } 1145 | 1146 | // Finally, set the value to the slice we built up 1147 | val.Set(valSlice) 1148 | 1149 | // If there were errors, we return those 1150 | if len(errors) > 0 { 1151 | return &Error{errors} 1152 | } 1153 | 1154 | return nil 1155 | } 1156 | 1157 | func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { 1158 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 1159 | dataValKind := dataVal.Kind() 1160 | valType := val.Type() 1161 | valElemType := valType.Elem() 1162 | arrayType := reflect.ArrayOf(valType.Len(), valElemType) 1163 | 1164 | valArray := val 1165 | 1166 | if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { 1167 | // Check input type 1168 | if dataValKind != reflect.Array && dataValKind != reflect.Slice { 1169 | if d.config.WeaklyTypedInput { 1170 | switch { 1171 | // Empty maps turn into empty arrays 1172 | case dataValKind == reflect.Map: 1173 | if dataVal.Len() == 0 { 1174 | val.Set(reflect.Zero(arrayType)) 1175 | return nil 1176 | } 1177 | 1178 | // All other types we try to convert to the array type 1179 | // and "lift" it into it. i.e. a string becomes a string array. 1180 | default: 1181 | // Just re-try this function with data as a slice. 1182 | return d.decodeArray(name, []interface{}{data}, val) 1183 | } 1184 | } 1185 | 1186 | return fmt.Errorf( 1187 | "'%s': source data must be an array or slice, got %s", name, dataValKind) 1188 | 1189 | } 1190 | if dataVal.Len() > arrayType.Len() { 1191 | return fmt.Errorf( 1192 | "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) 1193 | 1194 | } 1195 | 1196 | // Make a new array to hold our result, same size as the original data. 1197 | valArray = reflect.New(arrayType).Elem() 1198 | } 1199 | 1200 | // Accumulate any errors 1201 | errors := make([]string, 0) 1202 | 1203 | for i := 0; i < dataVal.Len(); i++ { 1204 | currentData := dataVal.Index(i).Interface() 1205 | currentField := valArray.Index(i) 1206 | 1207 | fieldName := name + "[" + strconv.Itoa(i) + "]" 1208 | if err := d.decode(fieldName, currentData, currentField); err != nil { 1209 | errors = appendErrors(errors, err) 1210 | } 1211 | } 1212 | 1213 | // Finally, set the value to the array we built up 1214 | val.Set(valArray) 1215 | 1216 | // If there were errors, we return those 1217 | if len(errors) > 0 { 1218 | return &Error{errors} 1219 | } 1220 | 1221 | return nil 1222 | } 1223 | 1224 | func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { 1225 | dataVal := reflect.Indirect(reflect.ValueOf(data)) 1226 | 1227 | // If the type of the value to write to and the data match directly, 1228 | // then we just set it directly instead of recursing into the structure. 1229 | if dataVal.Type() == val.Type() { 1230 | val.Set(dataVal) 1231 | return nil 1232 | } 1233 | 1234 | dataValKind := dataVal.Kind() 1235 | switch dataValKind { 1236 | case reflect.Map: 1237 | return d.decodeStructFromMap(name, dataVal, val) 1238 | 1239 | case reflect.Struct: 1240 | // Not the most efficient way to do this but we can optimize later if 1241 | // we want to. To convert from struct to struct we go to map first 1242 | // as an intermediary. 1243 | 1244 | // Make a new map to hold our result 1245 | mapType := reflect.TypeOf((map[string]interface{})(nil)) 1246 | mval := reflect.MakeMap(mapType) 1247 | 1248 | // Creating a pointer to a map so that other methods can completely 1249 | // overwrite the map if need be (looking at you decodeMapFromMap). The 1250 | // indirection allows the underlying map to be settable (CanSet() == true) 1251 | // where as reflect.MakeMap returns an unsettable map. 1252 | addrVal := reflect.New(mval.Type()) 1253 | 1254 | reflect.Indirect(addrVal).Set(mval) 1255 | if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil { 1256 | return err 1257 | } 1258 | 1259 | result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val) 1260 | return result 1261 | 1262 | default: 1263 | return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) 1264 | } 1265 | } 1266 | 1267 | func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { 1268 | dataValType := dataVal.Type() 1269 | if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { 1270 | return fmt.Errorf( 1271 | "'%s' needs a map with string keys, has '%s' keys", 1272 | name, dataValType.Key().Kind()) 1273 | } 1274 | 1275 | dataValKeys := make(map[reflect.Value]struct{}) 1276 | dataValKeysUnused := make(map[interface{}]struct{}) 1277 | for _, dataValKey := range dataVal.MapKeys() { 1278 | dataValKeys[dataValKey] = struct{}{} 1279 | dataValKeysUnused[dataValKey.Interface()] = struct{}{} 1280 | } 1281 | 1282 | targetValKeysUnused := make(map[interface{}]struct{}) 1283 | errors := make([]string, 0) 1284 | 1285 | // This slice will keep track of all the structs we'll be decoding. 1286 | // There can be more than one struct if there are embedded structs 1287 | // that are squashed. 1288 | structs := make([]reflect.Value, 1, 5) 1289 | structs[0] = val 1290 | 1291 | // Compile the list of all the fields that we're going to be decoding 1292 | // from all the structs. 1293 | type field struct { 1294 | field reflect.StructField 1295 | val reflect.Value 1296 | } 1297 | 1298 | // remainField is set to a valid field set with the "remain" tag if 1299 | // we are keeping track of remaining values. 1300 | var remainField *field 1301 | 1302 | fields := []field{} 1303 | for len(structs) > 0 { 1304 | structVal := structs[0] 1305 | structs = structs[1:] 1306 | 1307 | structType := structVal.Type() 1308 | 1309 | for i := 0; i < structType.NumField(); i++ { 1310 | fieldType := structType.Field(i) 1311 | fieldVal := structVal.Field(i) 1312 | if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct { 1313 | // Handle embedded struct pointers as embedded structs. 1314 | fieldVal = fieldVal.Elem() 1315 | } 1316 | 1317 | // If "squash" is specified in the tag, we squash the field down. 1318 | squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous 1319 | remain := false 1320 | 1321 | // We always parse the tags cause we're looking for other tags too 1322 | tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") 1323 | for _, tag := range tagParts[1:] { 1324 | if tag == "squash" { 1325 | squash = true 1326 | break 1327 | } 1328 | 1329 | if tag == "remain" { 1330 | remain = true 1331 | break 1332 | } 1333 | } 1334 | 1335 | if squash { 1336 | if fieldVal.Kind() != reflect.Struct { 1337 | errors = appendErrors(errors, 1338 | fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) 1339 | } else { 1340 | structs = append(structs, fieldVal) 1341 | } 1342 | continue 1343 | } 1344 | 1345 | // Build our field 1346 | if remain { 1347 | remainField = &field{fieldType, fieldVal} 1348 | } else { 1349 | // Normal struct field, store it away 1350 | fields = append(fields, field{fieldType, fieldVal}) 1351 | } 1352 | } 1353 | } 1354 | 1355 | // for fieldType, field := range fields { 1356 | for _, f := range fields { 1357 | field, fieldValue := f.field, f.val 1358 | fieldName := field.Name 1359 | 1360 | tagValue := field.Tag.Get(d.config.TagName) 1361 | tagValue = strings.SplitN(tagValue, ",", 2)[0] 1362 | if tagValue != "" { 1363 | fieldName = tagValue 1364 | } 1365 | 1366 | rawMapKey := reflect.ValueOf(fieldName) 1367 | rawMapVal := dataVal.MapIndex(rawMapKey) 1368 | if !rawMapVal.IsValid() { 1369 | // Do a slower search by iterating over each key and 1370 | // doing case-insensitive search. 1371 | for dataValKey := range dataValKeys { 1372 | mK, ok := dataValKey.Interface().(string) 1373 | if !ok { 1374 | // Not a string key 1375 | continue 1376 | } 1377 | 1378 | if d.config.MatchName(mK, fieldName) { 1379 | rawMapKey = dataValKey 1380 | rawMapVal = dataVal.MapIndex(dataValKey) 1381 | break 1382 | } 1383 | } 1384 | 1385 | if !rawMapVal.IsValid() { 1386 | // There was no matching key in the map for the value in 1387 | // the struct. Remember it for potential errors and metadata. 1388 | targetValKeysUnused[fieldName] = struct{}{} 1389 | continue 1390 | } 1391 | } 1392 | 1393 | if !fieldValue.IsValid() { 1394 | // This should never happen 1395 | panic("field is not valid") 1396 | } 1397 | 1398 | // If we can't set the field, then it is unexported or something, 1399 | // and we just continue onwards. 1400 | if !fieldValue.CanSet() { 1401 | continue 1402 | } 1403 | 1404 | // Delete the key we're using from the unused map so we stop tracking 1405 | delete(dataValKeysUnused, rawMapKey.Interface()) 1406 | 1407 | // If the name is empty string, then we're at the root, and we 1408 | // don't dot-join the fields. 1409 | if name != "" { 1410 | fieldName = name + "." + fieldName 1411 | } 1412 | 1413 | if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { 1414 | errors = appendErrors(errors, err) 1415 | } 1416 | } 1417 | 1418 | // If we have a "remain"-tagged field and we have unused keys then 1419 | // we put the unused keys directly into the remain field. 1420 | if remainField != nil && len(dataValKeysUnused) > 0 { 1421 | // Build a map of only the unused values 1422 | remain := map[interface{}]interface{}{} 1423 | for key := range dataValKeysUnused { 1424 | remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() 1425 | } 1426 | 1427 | // Decode it as-if we were just decoding this map onto our map. 1428 | if err := d.decodeMap(name, remain, remainField.val); err != nil { 1429 | errors = appendErrors(errors, err) 1430 | } 1431 | 1432 | // Set the map to nil so we have none so that the next check will 1433 | // not error (ErrorUnused) 1434 | dataValKeysUnused = nil 1435 | } 1436 | 1437 | if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { 1438 | keys := make([]string, 0, len(dataValKeysUnused)) 1439 | for rawKey := range dataValKeysUnused { 1440 | keys = append(keys, rawKey.(string)) 1441 | } 1442 | sort.Strings(keys) 1443 | 1444 | err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) 1445 | errors = appendErrors(errors, err) 1446 | } 1447 | 1448 | if d.config.ErrorUnset && len(targetValKeysUnused) > 0 { 1449 | keys := make([]string, 0, len(targetValKeysUnused)) 1450 | for rawKey := range targetValKeysUnused { 1451 | keys = append(keys, rawKey.(string)) 1452 | } 1453 | sort.Strings(keys) 1454 | 1455 | err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", ")) 1456 | errors = appendErrors(errors, err) 1457 | } 1458 | 1459 | if len(errors) > 0 { 1460 | return &Error{errors} 1461 | } 1462 | 1463 | // Add the unused keys to the list of unused keys if we're tracking metadata 1464 | if d.config.Metadata != nil { 1465 | for rawKey := range dataValKeysUnused { 1466 | key := rawKey.(string) 1467 | if name != "" { 1468 | key = name + "." + key 1469 | } 1470 | 1471 | d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) 1472 | } 1473 | for rawKey := range targetValKeysUnused { 1474 | key := rawKey.(string) 1475 | if name != "" { 1476 | key = name + "." + key 1477 | } 1478 | 1479 | d.config.Metadata.Unset = append(d.config.Metadata.Unset, key) 1480 | } 1481 | } 1482 | 1483 | return nil 1484 | } 1485 | 1486 | func isEmptyValue(v reflect.Value) bool { 1487 | switch getKind(v) { 1488 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 1489 | return v.Len() == 0 1490 | case reflect.Bool: 1491 | return !v.Bool() 1492 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1493 | return v.Int() == 0 1494 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1495 | return v.Uint() == 0 1496 | case reflect.Float32, reflect.Float64: 1497 | return v.Float() == 0 1498 | case reflect.Interface, reflect.Ptr: 1499 | return v.IsNil() 1500 | } 1501 | return false 1502 | } 1503 | 1504 | func getKind(val reflect.Value) reflect.Kind { 1505 | kind := val.Kind() 1506 | 1507 | switch { 1508 | case kind >= reflect.Int && kind <= reflect.Int64: 1509 | return reflect.Int 1510 | case kind >= reflect.Uint && kind <= reflect.Uint64: 1511 | return reflect.Uint 1512 | case kind >= reflect.Float32 && kind <= reflect.Float64: 1513 | return reflect.Float32 1514 | default: 1515 | return kind 1516 | } 1517 | } 1518 | 1519 | func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool { 1520 | for i := 0; i < typ.NumField(); i++ { 1521 | f := typ.Field(i) 1522 | if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields 1523 | return true 1524 | } 1525 | if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside 1526 | return true 1527 | } 1528 | } 1529 | return false 1530 | } 1531 | 1532 | func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { 1533 | if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { 1534 | return v 1535 | } 1536 | deref := v.Elem() 1537 | derefT := deref.Type() 1538 | if isStructTypeConvertibleToMap(derefT, true, tagName) { 1539 | return deref 1540 | } 1541 | return v 1542 | } 1543 | -------------------------------------------------------------------------------- /mapstructure_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | type Person struct { 9 | Name string 10 | Age int 11 | Emails []string 12 | Extra map[string]string 13 | } 14 | 15 | func Benchmark_Decode(b *testing.B) { 16 | input := map[string]interface{}{ 17 | "name": "Mitchell", 18 | "age": 91, 19 | "emails": []string{"one", "two", "three"}, 20 | "extra": map[string]string{ 21 | "twitter": "mitchellh", 22 | }, 23 | } 24 | 25 | var result Person 26 | for i := 0; i < b.N; i++ { 27 | Decode(input, &result) 28 | } 29 | } 30 | 31 | // decodeViaJSON takes the map data and passes it through encoding/json to convert it into the 32 | // given Go native structure pointed to by v. v must be a pointer to a struct. 33 | func decodeViaJSON(data interface{}, v interface{}) error { 34 | // Perform the task by simply marshalling the input into JSON, 35 | // then unmarshalling it into target native Go struct. 36 | b, err := json.Marshal(data) 37 | if err != nil { 38 | return err 39 | } 40 | return json.Unmarshal(b, v) 41 | } 42 | 43 | func Benchmark_DecodeViaJSON(b *testing.B) { 44 | input := map[string]interface{}{ 45 | "name": "Mitchell", 46 | "age": 91, 47 | "emails": []string{"one", "two", "three"}, 48 | "extra": map[string]string{ 49 | "twitter": "mitchellh", 50 | }, 51 | } 52 | 53 | var result Person 54 | for i := 0; i < b.N; i++ { 55 | decodeViaJSON(input, &result) 56 | } 57 | } 58 | 59 | func Benchmark_JSONUnmarshal(b *testing.B) { 60 | input := map[string]interface{}{ 61 | "name": "Mitchell", 62 | "age": 91, 63 | "emails": []string{"one", "two", "three"}, 64 | "extra": map[string]string{ 65 | "twitter": "mitchellh", 66 | }, 67 | } 68 | 69 | inputB, err := json.Marshal(input) 70 | if err != nil { 71 | b.Fatal("Failed to marshal test input:", err) 72 | } 73 | 74 | var result Person 75 | for i := 0; i < b.N; i++ { 76 | json.Unmarshal(inputB, &result) 77 | } 78 | } 79 | 80 | func Benchmark_DecodeBasic(b *testing.B) { 81 | input := map[string]interface{}{ 82 | "vstring": "foo", 83 | "vint": 42, 84 | "Vuint": 42, 85 | "vbool": true, 86 | "Vfloat": 42.42, 87 | "vsilent": true, 88 | "vdata": 42, 89 | "vjsonInt": json.Number("1234"), 90 | "vjsonFloat": json.Number("1234.5"), 91 | "vjsonNumber": json.Number("1234.5"), 92 | } 93 | 94 | for i := 0; i < b.N; i++ { 95 | var result Basic 96 | Decode(input, &result) 97 | } 98 | } 99 | 100 | func Benchmark_DecodeEmbedded(b *testing.B) { 101 | input := map[string]interface{}{ 102 | "vstring": "foo", 103 | "Basic": map[string]interface{}{ 104 | "vstring": "innerfoo", 105 | }, 106 | "vunique": "bar", 107 | } 108 | 109 | var result Embedded 110 | for i := 0; i < b.N; i++ { 111 | Decode(input, &result) 112 | } 113 | } 114 | 115 | func Benchmark_DecodeTypeConversion(b *testing.B) { 116 | input := map[string]interface{}{ 117 | "IntToFloat": 42, 118 | "IntToUint": 42, 119 | "IntToBool": 1, 120 | "IntToString": 42, 121 | "UintToInt": 42, 122 | "UintToFloat": 42, 123 | "UintToBool": 42, 124 | "UintToString": 42, 125 | "BoolToInt": true, 126 | "BoolToUint": true, 127 | "BoolToFloat": true, 128 | "BoolToString": true, 129 | "FloatToInt": 42.42, 130 | "FloatToUint": 42.42, 131 | "FloatToBool": 42.42, 132 | "FloatToString": 42.42, 133 | "StringToInt": "42", 134 | "StringToUint": "42", 135 | "StringToBool": "1", 136 | "StringToFloat": "42.42", 137 | "SliceToMap": []interface{}{}, 138 | "MapToSlice": map[string]interface{}{}, 139 | } 140 | 141 | var resultStrict TypeConversionResult 142 | for i := 0; i < b.N; i++ { 143 | Decode(input, &resultStrict) 144 | } 145 | } 146 | 147 | func Benchmark_DecodeMap(b *testing.B) { 148 | input := map[string]interface{}{ 149 | "vfoo": "foo", 150 | "vother": map[interface{}]interface{}{ 151 | "foo": "foo", 152 | "bar": "bar", 153 | }, 154 | } 155 | 156 | var result Map 157 | for i := 0; i < b.N; i++ { 158 | Decode(input, &result) 159 | } 160 | } 161 | 162 | func Benchmark_DecodeMapOfStruct(b *testing.B) { 163 | input := map[string]interface{}{ 164 | "value": map[string]interface{}{ 165 | "foo": map[string]string{"vstring": "one"}, 166 | "bar": map[string]string{"vstring": "two"}, 167 | }, 168 | } 169 | 170 | var result MapOfStruct 171 | for i := 0; i < b.N; i++ { 172 | Decode(input, &result) 173 | } 174 | } 175 | 176 | func Benchmark_DecodeSlice(b *testing.B) { 177 | input := map[string]interface{}{ 178 | "vfoo": "foo", 179 | "vbar": []string{"foo", "bar", "baz"}, 180 | } 181 | 182 | var result Slice 183 | for i := 0; i < b.N; i++ { 184 | Decode(input, &result) 185 | } 186 | } 187 | 188 | func Benchmark_DecodeSliceOfStruct(b *testing.B) { 189 | input := map[string]interface{}{ 190 | "value": []map[string]interface{}{ 191 | {"vstring": "one"}, 192 | {"vstring": "two"}, 193 | }, 194 | } 195 | 196 | var result SliceOfStruct 197 | for i := 0; i < b.N; i++ { 198 | Decode(input, &result) 199 | } 200 | } 201 | 202 | func Benchmark_DecodeWeaklyTypedInput(b *testing.B) { 203 | // This input can come from anywhere, but typically comes from 204 | // something like decoding JSON, generated by a weakly typed language 205 | // such as PHP. 206 | input := map[string]interface{}{ 207 | "name": 123, // number => string 208 | "age": "42", // string => number 209 | "emails": map[string]interface{}{}, // empty map => empty array 210 | } 211 | 212 | var result Person 213 | config := &DecoderConfig{ 214 | WeaklyTypedInput: true, 215 | Result: &result, 216 | } 217 | 218 | decoder, err := NewDecoder(config) 219 | if err != nil { 220 | panic(err) 221 | } 222 | 223 | for i := 0; i < b.N; i++ { 224 | decoder.Decode(input) 225 | } 226 | } 227 | 228 | func Benchmark_DecodeMetadata(b *testing.B) { 229 | input := map[string]interface{}{ 230 | "name": "Mitchell", 231 | "age": 91, 232 | "email": "foo@bar.com", 233 | } 234 | 235 | var md Metadata 236 | var result Person 237 | config := &DecoderConfig{ 238 | Metadata: &md, 239 | Result: &result, 240 | } 241 | 242 | decoder, err := NewDecoder(config) 243 | if err != nil { 244 | panic(err) 245 | } 246 | 247 | for i := 0; i < b.N; i++ { 248 | decoder.Decode(input) 249 | } 250 | } 251 | 252 | func Benchmark_DecodeMetadataEmbedded(b *testing.B) { 253 | input := map[string]interface{}{ 254 | "vstring": "foo", 255 | "vunique": "bar", 256 | } 257 | 258 | var md Metadata 259 | var result EmbeddedSquash 260 | config := &DecoderConfig{ 261 | Metadata: &md, 262 | Result: &result, 263 | } 264 | 265 | decoder, err := NewDecoder(config) 266 | if err != nil { 267 | b.Fatalf("err: %s", err) 268 | } 269 | 270 | for i := 0; i < b.N; i++ { 271 | decoder.Decode(input) 272 | } 273 | } 274 | 275 | func Benchmark_DecodeTagged(b *testing.B) { 276 | input := map[string]interface{}{ 277 | "foo": "bar", 278 | "bar": "value", 279 | } 280 | 281 | var result Tagged 282 | for i := 0; i < b.N; i++ { 283 | Decode(input, &result) 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /mapstructure_bugs_test.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // GH-1, GH-10, GH-96 10 | func TestDecode_NilValue(t *testing.T) { 11 | t.Parallel() 12 | 13 | tests := []struct { 14 | name string 15 | in interface{} 16 | target interface{} 17 | out interface{} 18 | metaKeys []string 19 | metaUnused []string 20 | }{ 21 | { 22 | "all nil", 23 | &map[string]interface{}{ 24 | "vfoo": nil, 25 | "vother": nil, 26 | }, 27 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 28 | &Map{Vfoo: "", Vother: nil}, 29 | []string{"Vfoo", "Vother"}, 30 | []string{}, 31 | }, 32 | { 33 | "partial nil", 34 | &map[string]interface{}{ 35 | "vfoo": "baz", 36 | "vother": nil, 37 | }, 38 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 39 | &Map{Vfoo: "baz", Vother: nil}, 40 | []string{"Vfoo", "Vother"}, 41 | []string{}, 42 | }, 43 | { 44 | "partial decode", 45 | &map[string]interface{}{ 46 | "vother": nil, 47 | }, 48 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 49 | &Map{Vfoo: "foo", Vother: nil}, 50 | []string{"Vother"}, 51 | []string{}, 52 | }, 53 | { 54 | "unused values", 55 | &map[string]interface{}{ 56 | "vbar": "bar", 57 | "vfoo": nil, 58 | "vother": nil, 59 | }, 60 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 61 | &Map{Vfoo: "", Vother: nil}, 62 | []string{"Vfoo", "Vother"}, 63 | []string{"vbar"}, 64 | }, 65 | { 66 | "map interface all nil", 67 | &map[interface{}]interface{}{ 68 | "vfoo": nil, 69 | "vother": nil, 70 | }, 71 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 72 | &Map{Vfoo: "", Vother: nil}, 73 | []string{"Vfoo", "Vother"}, 74 | []string{}, 75 | }, 76 | { 77 | "map interface partial nil", 78 | &map[interface{}]interface{}{ 79 | "vfoo": "baz", 80 | "vother": nil, 81 | }, 82 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 83 | &Map{Vfoo: "baz", Vother: nil}, 84 | []string{"Vfoo", "Vother"}, 85 | []string{}, 86 | }, 87 | { 88 | "map interface partial decode", 89 | &map[interface{}]interface{}{ 90 | "vother": nil, 91 | }, 92 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 93 | &Map{Vfoo: "foo", Vother: nil}, 94 | []string{"Vother"}, 95 | []string{}, 96 | }, 97 | { 98 | "map interface unused values", 99 | &map[interface{}]interface{}{ 100 | "vbar": "bar", 101 | "vfoo": nil, 102 | "vother": nil, 103 | }, 104 | &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}}, 105 | &Map{Vfoo: "", Vother: nil}, 106 | []string{"Vfoo", "Vother"}, 107 | []string{"vbar"}, 108 | }, 109 | } 110 | 111 | for _, tc := range tests { 112 | t.Run(tc.name, func(t *testing.T) { 113 | config := &DecoderConfig{ 114 | Metadata: new(Metadata), 115 | Result: tc.target, 116 | ZeroFields: true, 117 | } 118 | 119 | decoder, err := NewDecoder(config) 120 | if err != nil { 121 | t.Fatalf("should not error: %s", err) 122 | } 123 | 124 | err = decoder.Decode(tc.in) 125 | if err != nil { 126 | t.Fatalf("should not error: %s", err) 127 | } 128 | 129 | if !reflect.DeepEqual(tc.out, tc.target) { 130 | t.Fatalf("%q: TestDecode_NilValue() expected: %#v, got: %#v", tc.name, tc.out, tc.target) 131 | } 132 | 133 | if !reflect.DeepEqual(tc.metaKeys, config.Metadata.Keys) { 134 | t.Fatalf("%q: Metadata.Keys mismatch expected: %#v, got: %#v", tc.name, tc.metaKeys, config.Metadata.Keys) 135 | } 136 | 137 | if !reflect.DeepEqual(tc.metaUnused, config.Metadata.Unused) { 138 | t.Fatalf("%q: Metadata.Unused mismatch expected: %#v, got: %#v", tc.name, tc.metaUnused, config.Metadata.Unused) 139 | } 140 | }) 141 | } 142 | } 143 | 144 | // #48 145 | func TestNestedTypePointerWithDefaults(t *testing.T) { 146 | t.Parallel() 147 | 148 | input := map[string]interface{}{ 149 | "vfoo": "foo", 150 | "vbar": map[string]interface{}{ 151 | "vstring": "foo", 152 | "vint": 42, 153 | "vbool": true, 154 | }, 155 | } 156 | 157 | result := NestedPointer{ 158 | Vbar: &Basic{ 159 | Vuint: 42, 160 | }, 161 | } 162 | err := Decode(input, &result) 163 | if err != nil { 164 | t.Fatalf("got an err: %s", err.Error()) 165 | } 166 | 167 | if result.Vfoo != "foo" { 168 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 169 | } 170 | 171 | if result.Vbar.Vstring != "foo" { 172 | t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring) 173 | } 174 | 175 | if result.Vbar.Vint != 42 { 176 | t.Errorf("vint value should be 42: %#v", result.Vbar.Vint) 177 | } 178 | 179 | if result.Vbar.Vbool != true { 180 | t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool) 181 | } 182 | 183 | if result.Vbar.Vextra != "" { 184 | t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra) 185 | } 186 | 187 | // this is the error 188 | if result.Vbar.Vuint != 42 { 189 | t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint) 190 | } 191 | 192 | } 193 | 194 | type NestedSlice struct { 195 | Vfoo string 196 | Vbars []Basic 197 | Vempty []Basic 198 | } 199 | 200 | // #48 201 | func TestNestedTypeSliceWithDefaults(t *testing.T) { 202 | t.Parallel() 203 | 204 | input := map[string]interface{}{ 205 | "vfoo": "foo", 206 | "vbars": []map[string]interface{}{ 207 | {"vstring": "foo", "vint": 42, "vbool": true}, 208 | {"vint": 42, "vbool": true}, 209 | }, 210 | "vempty": []map[string]interface{}{ 211 | {"vstring": "foo", "vint": 42, "vbool": true}, 212 | {"vint": 42, "vbool": true}, 213 | }, 214 | } 215 | 216 | result := NestedSlice{ 217 | Vbars: []Basic{ 218 | {Vuint: 42}, 219 | {Vstring: "foo"}, 220 | }, 221 | } 222 | err := Decode(input, &result) 223 | if err != nil { 224 | t.Fatalf("got an err: %s", err.Error()) 225 | } 226 | 227 | if result.Vfoo != "foo" { 228 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 229 | } 230 | 231 | if result.Vbars[0].Vstring != "foo" { 232 | t.Errorf("vstring value should be 'foo': %#v", result.Vbars[0].Vstring) 233 | } 234 | // this is the error 235 | if result.Vbars[0].Vuint != 42 { 236 | t.Errorf("vuint value should be 42: %#v", result.Vbars[0].Vuint) 237 | } 238 | } 239 | 240 | // #48 workaround 241 | func TestNestedTypeWithDefaults(t *testing.T) { 242 | t.Parallel() 243 | 244 | input := map[string]interface{}{ 245 | "vfoo": "foo", 246 | "vbar": map[string]interface{}{ 247 | "vstring": "foo", 248 | "vint": 42, 249 | "vbool": true, 250 | }, 251 | } 252 | 253 | result := Nested{ 254 | Vbar: Basic{ 255 | Vuint: 42, 256 | }, 257 | } 258 | err := Decode(input, &result) 259 | if err != nil { 260 | t.Fatalf("got an err: %s", err.Error()) 261 | } 262 | 263 | if result.Vfoo != "foo" { 264 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 265 | } 266 | 267 | if result.Vbar.Vstring != "foo" { 268 | t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring) 269 | } 270 | 271 | if result.Vbar.Vint != 42 { 272 | t.Errorf("vint value should be 42: %#v", result.Vbar.Vint) 273 | } 274 | 275 | if result.Vbar.Vbool != true { 276 | t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool) 277 | } 278 | 279 | if result.Vbar.Vextra != "" { 280 | t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra) 281 | } 282 | 283 | // this is the error 284 | if result.Vbar.Vuint != 42 { 285 | t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint) 286 | } 287 | 288 | } 289 | 290 | // #67 panic() on extending slices (decodeSlice with disabled ZeroValues) 291 | func TestDecodeSliceToEmptySliceWOZeroing(t *testing.T) { 292 | t.Parallel() 293 | 294 | type TestStruct struct { 295 | Vfoo []string 296 | } 297 | 298 | decode := func(m interface{}, rawVal interface{}) error { 299 | config := &DecoderConfig{ 300 | Metadata: nil, 301 | Result: rawVal, 302 | ZeroFields: false, 303 | } 304 | 305 | decoder, err := NewDecoder(config) 306 | if err != nil { 307 | return err 308 | } 309 | 310 | return decoder.Decode(m) 311 | } 312 | 313 | { 314 | input := map[string]interface{}{ 315 | "vfoo": []string{"1"}, 316 | } 317 | 318 | result := &TestStruct{} 319 | 320 | err := decode(input, &result) 321 | if err != nil { 322 | t.Fatalf("got an err: %s", err.Error()) 323 | } 324 | } 325 | 326 | { 327 | input := map[string]interface{}{ 328 | "vfoo": []string{"1"}, 329 | } 330 | 331 | result := &TestStruct{ 332 | Vfoo: []string{}, 333 | } 334 | 335 | err := decode(input, &result) 336 | if err != nil { 337 | t.Fatalf("got an err: %s", err.Error()) 338 | } 339 | } 340 | 341 | { 342 | input := map[string]interface{}{ 343 | "vfoo": []string{"2", "3"}, 344 | } 345 | 346 | result := &TestStruct{ 347 | Vfoo: []string{"1"}, 348 | } 349 | 350 | err := decode(input, &result) 351 | if err != nil { 352 | t.Fatalf("got an err: %s", err.Error()) 353 | } 354 | } 355 | } 356 | 357 | // #70 358 | func TestNextSquashMapstructure(t *testing.T) { 359 | data := &struct { 360 | Level1 struct { 361 | Level2 struct { 362 | Foo string 363 | } `mapstructure:",squash"` 364 | } `mapstructure:",squash"` 365 | }{} 366 | err := Decode(map[interface{}]interface{}{"foo": "baz"}, &data) 367 | if err != nil { 368 | t.Fatalf("should not error: %s", err) 369 | } 370 | if data.Level1.Level2.Foo != "baz" { 371 | t.Fatal("value should be baz") 372 | } 373 | } 374 | 375 | type ImplementsInterfacePointerReceiver struct { 376 | Name string 377 | } 378 | 379 | func (i *ImplementsInterfacePointerReceiver) DoStuff() {} 380 | 381 | type ImplementsInterfaceValueReceiver string 382 | 383 | func (i ImplementsInterfaceValueReceiver) DoStuff() {} 384 | 385 | // GH-140 Type error when using DecodeHook to decode into interface 386 | func TestDecode_DecodeHookInterface(t *testing.T) { 387 | t.Parallel() 388 | 389 | type Interface interface { 390 | DoStuff() 391 | } 392 | type DecodeIntoInterface struct { 393 | Test Interface 394 | } 395 | 396 | testData := map[string]string{"test": "test"} 397 | 398 | stringToPointerInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) { 399 | if from.Kind() != reflect.String { 400 | return data, nil 401 | } 402 | 403 | if to != reflect.TypeOf((*Interface)(nil)).Elem() { 404 | return data, nil 405 | } 406 | // Ensure interface is satisfied 407 | var impl Interface = &ImplementsInterfacePointerReceiver{data.(string)} 408 | return impl, nil 409 | } 410 | 411 | stringToValueInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) { 412 | if from.Kind() != reflect.String { 413 | return data, nil 414 | } 415 | 416 | if to != reflect.TypeOf((*Interface)(nil)).Elem() { 417 | return data, nil 418 | } 419 | // Ensure interface is satisfied 420 | var impl Interface = ImplementsInterfaceValueReceiver(data.(string)) 421 | return impl, nil 422 | } 423 | 424 | { 425 | decodeInto := new(DecodeIntoInterface) 426 | 427 | decoder, _ := NewDecoder(&DecoderConfig{ 428 | DecodeHook: stringToPointerInterfaceDecodeHook, 429 | Result: decodeInto, 430 | }) 431 | 432 | err := decoder.Decode(testData) 433 | if err != nil { 434 | t.Fatalf("Decode returned error: %s", err) 435 | } 436 | 437 | expected := &ImplementsInterfacePointerReceiver{"test"} 438 | if !reflect.DeepEqual(decodeInto.Test, expected) { 439 | t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected) 440 | } 441 | } 442 | 443 | { 444 | decodeInto := new(DecodeIntoInterface) 445 | 446 | decoder, _ := NewDecoder(&DecoderConfig{ 447 | DecodeHook: stringToValueInterfaceDecodeHook, 448 | Result: decodeInto, 449 | }) 450 | 451 | err := decoder.Decode(testData) 452 | if err != nil { 453 | t.Fatalf("Decode returned error: %s", err) 454 | } 455 | 456 | expected := ImplementsInterfaceValueReceiver("test") 457 | if !reflect.DeepEqual(decodeInto.Test, expected) { 458 | t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected) 459 | } 460 | } 461 | } 462 | 463 | // #103 Check for data type before trying to access its composants prevent a panic error 464 | // in decodeSlice 465 | func TestDecodeBadDataTypeInSlice(t *testing.T) { 466 | t.Parallel() 467 | 468 | input := map[string]interface{}{ 469 | "Toto": "titi", 470 | } 471 | result := []struct { 472 | Toto string 473 | }{} 474 | 475 | if err := Decode(input, &result); err == nil { 476 | t.Error("An error was expected, got nil") 477 | } 478 | } 479 | 480 | // #202 Ensure that intermediate maps in the struct -> struct decode process are settable 481 | // and not just the elements within them. 482 | func TestDecodeIntermediateMapsSettable(t *testing.T) { 483 | type Timestamp struct { 484 | Seconds int64 485 | Nanos int32 486 | } 487 | 488 | type TsWrapper struct { 489 | Timestamp *Timestamp 490 | } 491 | 492 | type TimeWrapper struct { 493 | Timestamp time.Time 494 | } 495 | 496 | input := TimeWrapper{ 497 | Timestamp: time.Unix(123456789, 987654), 498 | } 499 | 500 | expected := TsWrapper{ 501 | Timestamp: &Timestamp{ 502 | Seconds: 123456789, 503 | Nanos: 987654, 504 | }, 505 | } 506 | 507 | timePtrType := reflect.TypeOf((*time.Time)(nil)) 508 | mapStrInfType := reflect.TypeOf((map[string]interface{})(nil)) 509 | 510 | var actual TsWrapper 511 | decoder, err := NewDecoder(&DecoderConfig{ 512 | Result: &actual, 513 | DecodeHook: func(from, to reflect.Type, data interface{}) (interface{}, error) { 514 | if from == timePtrType && to == mapStrInfType { 515 | ts := data.(*time.Time) 516 | nanos := ts.UnixNano() 517 | 518 | seconds := nanos / 1000000000 519 | nanos = nanos % 1000000000 520 | 521 | return &map[string]interface{}{ 522 | "Seconds": seconds, 523 | "Nanos": int32(nanos), 524 | }, nil 525 | } 526 | return data, nil 527 | }, 528 | }) 529 | 530 | if err != nil { 531 | t.Fatalf("failed to create decoder: %v", err) 532 | } 533 | 534 | if err := decoder.Decode(&input); err != nil { 535 | t.Fatalf("failed to decode input: %v", err) 536 | } 537 | 538 | if !reflect.DeepEqual(expected, actual) { 539 | t.Fatalf("expected: %#[1]v (%[1]T), got: %#[2]v (%[2]T)", expected, actual) 540 | } 541 | } 542 | 543 | // GH-206: decodeInt throws an error for an empty string 544 | func TestDecode_weakEmptyStringToInt(t *testing.T) { 545 | input := map[string]interface{}{ 546 | "StringToInt": "", 547 | "StringToUint": "", 548 | "StringToBool": "", 549 | "StringToFloat": "", 550 | } 551 | 552 | expectedResultWeak := TypeConversionResult{ 553 | StringToInt: 0, 554 | StringToUint: 0, 555 | StringToBool: false, 556 | StringToFloat: 0, 557 | } 558 | 559 | // Test weak type conversion 560 | var resultWeak TypeConversionResult 561 | err := WeakDecode(input, &resultWeak) 562 | if err != nil { 563 | t.Fatalf("got an err: %s", err) 564 | } 565 | 566 | if !reflect.DeepEqual(resultWeak, expectedResultWeak) { 567 | t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak) 568 | } 569 | } 570 | 571 | // GH-228: Squash cause *time.Time set to zero 572 | func TestMapSquash(t *testing.T) { 573 | type AA struct { 574 | T *time.Time 575 | } 576 | type A struct { 577 | AA 578 | } 579 | 580 | v := time.Now() 581 | in := &AA{ 582 | T: &v, 583 | } 584 | out := &A{} 585 | d, err := NewDecoder(&DecoderConfig{ 586 | Squash: true, 587 | Result: out, 588 | }) 589 | if err != nil { 590 | t.Fatalf("err: %s", err) 591 | } 592 | if err := d.Decode(in); err != nil { 593 | t.Fatalf("err: %s", err) 594 | } 595 | 596 | // these failed 597 | if !v.Equal(*out.T) { 598 | t.Fatal("expected equal") 599 | } 600 | if out.T.IsZero() { 601 | t.Fatal("expected false") 602 | } 603 | } 604 | 605 | // GH-238: Empty key name when decoding map from struct with only omitempty flag 606 | func TestMapOmitEmptyWithEmptyFieldnameInTag(t *testing.T) { 607 | type Struct struct { 608 | Username string `mapstructure:",omitempty"` 609 | Age int `mapstructure:",omitempty"` 610 | } 611 | 612 | s := Struct{ 613 | Username: "Joe", 614 | } 615 | var m map[string]interface{} 616 | 617 | if err := Decode(s, &m); err != nil { 618 | t.Fatal(err) 619 | } 620 | 621 | if len(m) != 1 { 622 | t.Fatalf("fail: %#v", m) 623 | } 624 | if m["Username"] != "Joe" { 625 | t.Fatalf("fail: %#v", m) 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /mapstructure_examples_test.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ExampleDecode() { 8 | type Person struct { 9 | Name string 10 | Age int 11 | Emails []string 12 | Extra map[string]string 13 | } 14 | 15 | // This input can come from anywhere, but typically comes from 16 | // something like decoding JSON where we're not quite sure of the 17 | // struct initially. 18 | input := map[string]interface{}{ 19 | "name": "Mitchell", 20 | "age": 91, 21 | "emails": []string{"one", "two", "three"}, 22 | "extra": map[string]string{ 23 | "twitter": "mitchellh", 24 | }, 25 | } 26 | 27 | var result Person 28 | err := Decode(input, &result) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | fmt.Printf("%#v", result) 34 | // Output: 35 | // mapstructure.Person{Name:"Mitchell", Age:91, Emails:[]string{"one", "two", "three"}, Extra:map[string]string{"twitter":"mitchellh"}} 36 | } 37 | 38 | func ExampleDecode_errors() { 39 | type Person struct { 40 | Name string 41 | Age int 42 | Emails []string 43 | Extra map[string]string 44 | } 45 | 46 | // This input can come from anywhere, but typically comes from 47 | // something like decoding JSON where we're not quite sure of the 48 | // struct initially. 49 | input := map[string]interface{}{ 50 | "name": 123, 51 | "age": "bad value", 52 | "emails": []int{1, 2, 3}, 53 | } 54 | 55 | var result Person 56 | err := Decode(input, &result) 57 | if err == nil { 58 | panic("should have an error") 59 | } 60 | 61 | fmt.Println(err.Error()) 62 | // Output: 63 | // 5 error(s) decoding: 64 | // 65 | // * 'Age' expected type 'int', got unconvertible type 'string', value: 'bad value' 66 | // * 'Emails[0]' expected type 'string', got unconvertible type 'int', value: '1' 67 | // * 'Emails[1]' expected type 'string', got unconvertible type 'int', value: '2' 68 | // * 'Emails[2]' expected type 'string', got unconvertible type 'int', value: '3' 69 | // * 'Name' expected type 'string', got unconvertible type 'int', value: '123' 70 | } 71 | 72 | func ExampleDecode_metadata() { 73 | type Person struct { 74 | Name string 75 | Age int 76 | } 77 | 78 | // This input can come from anywhere, but typically comes from 79 | // something like decoding JSON where we're not quite sure of the 80 | // struct initially. 81 | input := map[string]interface{}{ 82 | "name": "Mitchell", 83 | "age": 91, 84 | "email": "foo@bar.com", 85 | } 86 | 87 | // For metadata, we make a more advanced DecoderConfig so we can 88 | // more finely configure the decoder that is used. In this case, we 89 | // just tell the decoder we want to track metadata. 90 | var md Metadata 91 | var result Person 92 | config := &DecoderConfig{ 93 | Metadata: &md, 94 | Result: &result, 95 | } 96 | 97 | decoder, err := NewDecoder(config) 98 | if err != nil { 99 | panic(err) 100 | } 101 | 102 | if err := decoder.Decode(input); err != nil { 103 | panic(err) 104 | } 105 | 106 | fmt.Printf("Unused keys: %#v", md.Unused) 107 | // Output: 108 | // Unused keys: []string{"email"} 109 | } 110 | 111 | func ExampleDecode_weaklyTypedInput() { 112 | type Person struct { 113 | Name string 114 | Age int 115 | Emails []string 116 | } 117 | 118 | // This input can come from anywhere, but typically comes from 119 | // something like decoding JSON, generated by a weakly typed language 120 | // such as PHP. 121 | input := map[string]interface{}{ 122 | "name": 123, // number => string 123 | "age": "42", // string => number 124 | "emails": map[string]interface{}{}, // empty map => empty array 125 | } 126 | 127 | var result Person 128 | config := &DecoderConfig{ 129 | WeaklyTypedInput: true, 130 | Result: &result, 131 | } 132 | 133 | decoder, err := NewDecoder(config) 134 | if err != nil { 135 | panic(err) 136 | } 137 | 138 | err = decoder.Decode(input) 139 | if err != nil { 140 | panic(err) 141 | } 142 | 143 | fmt.Printf("%#v", result) 144 | // Output: mapstructure.Person{Name:"123", Age:42, Emails:[]string{}} 145 | } 146 | 147 | func ExampleDecode_tags() { 148 | // Note that the mapstructure tags defined in the struct type 149 | // can indicate which fields the values are mapped to. 150 | type Person struct { 151 | Name string `mapstructure:"person_name"` 152 | Age int `mapstructure:"person_age"` 153 | } 154 | 155 | input := map[string]interface{}{ 156 | "person_name": "Mitchell", 157 | "person_age": 91, 158 | } 159 | 160 | var result Person 161 | err := Decode(input, &result) 162 | if err != nil { 163 | panic(err) 164 | } 165 | 166 | fmt.Printf("%#v", result) 167 | // Output: 168 | // mapstructure.Person{Name:"Mitchell", Age:91} 169 | } 170 | 171 | func ExampleDecode_embeddedStruct() { 172 | // Squashing multiple embedded structs is allowed using the squash tag. 173 | // This is demonstrated by creating a composite struct of multiple types 174 | // and decoding into it. In this case, a person can carry with it both 175 | // a Family and a Location, as well as their own FirstName. 176 | type Family struct { 177 | LastName string 178 | } 179 | type Location struct { 180 | City string 181 | } 182 | type Person struct { 183 | Family `mapstructure:",squash"` 184 | Location `mapstructure:",squash"` 185 | FirstName string 186 | } 187 | 188 | input := map[string]interface{}{ 189 | "FirstName": "Mitchell", 190 | "LastName": "Hashimoto", 191 | "City": "San Francisco", 192 | } 193 | 194 | var result Person 195 | err := Decode(input, &result) 196 | if err != nil { 197 | panic(err) 198 | } 199 | 200 | fmt.Printf("%s %s, %s", result.FirstName, result.LastName, result.City) 201 | // Output: 202 | // Mitchell Hashimoto, San Francisco 203 | } 204 | 205 | func ExampleDecode_remainingData() { 206 | // Note that the mapstructure tags defined in the struct type 207 | // can indicate which fields the values are mapped to. 208 | type Person struct { 209 | Name string 210 | Age int 211 | Other map[string]interface{} `mapstructure:",remain"` 212 | } 213 | 214 | input := map[string]interface{}{ 215 | "name": "Mitchell", 216 | "age": 91, 217 | "email": "mitchell@example.com", 218 | } 219 | 220 | var result Person 221 | err := Decode(input, &result) 222 | if err != nil { 223 | panic(err) 224 | } 225 | 226 | fmt.Printf("%#v", result) 227 | // Output: 228 | // mapstructure.Person{Name:"Mitchell", Age:91, Other:map[string]interface {}{"email":"mitchell@example.com"}} 229 | } 230 | 231 | func ExampleDecode_omitempty() { 232 | // Add omitempty annotation to avoid map keys for empty values 233 | type Family struct { 234 | LastName string 235 | } 236 | type Location struct { 237 | City string 238 | } 239 | type Person struct { 240 | *Family `mapstructure:",omitempty"` 241 | *Location `mapstructure:",omitempty"` 242 | Age int 243 | FirstName string 244 | } 245 | 246 | result := &map[string]interface{}{} 247 | input := Person{FirstName: "Somebody"} 248 | err := Decode(input, &result) 249 | if err != nil { 250 | panic(err) 251 | } 252 | 253 | fmt.Printf("%+v", result) 254 | // Output: 255 | // &map[Age:0 FirstName:Somebody] 256 | } 257 | -------------------------------------------------------------------------------- /mapstructure_test.go: -------------------------------------------------------------------------------- 1 | package mapstructure 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "reflect" 7 | "sort" 8 | "strings" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | type Basic struct { 14 | Vstring string 15 | Vint int 16 | Vint8 int8 17 | Vint16 int16 18 | Vint32 int32 19 | Vint64 int64 20 | Vuint uint 21 | Vbool bool 22 | Vfloat float64 23 | Vextra string 24 | vsilent bool 25 | Vdata interface{} 26 | VjsonInt int 27 | VjsonUint uint 28 | VjsonUint64 uint64 29 | VjsonFloat float64 30 | VjsonNumber json.Number 31 | } 32 | 33 | type BasicPointer struct { 34 | Vstring *string 35 | Vint *int 36 | Vuint *uint 37 | Vbool *bool 38 | Vfloat *float64 39 | Vextra *string 40 | vsilent *bool 41 | Vdata *interface{} 42 | VjsonInt *int 43 | VjsonFloat *float64 44 | VjsonNumber *json.Number 45 | } 46 | 47 | type BasicSquash struct { 48 | Test Basic `mapstructure:",squash"` 49 | } 50 | 51 | type Embedded struct { 52 | Basic 53 | Vunique string 54 | } 55 | 56 | type EmbeddedPointer struct { 57 | *Basic 58 | Vunique string 59 | } 60 | 61 | type EmbeddedSquash struct { 62 | Basic `mapstructure:",squash"` 63 | Vunique string 64 | } 65 | 66 | type EmbeddedPointerSquash struct { 67 | *Basic `mapstructure:",squash"` 68 | Vunique string 69 | } 70 | 71 | type BasicMapStructure struct { 72 | Vunique string `mapstructure:"vunique"` 73 | Vtime *time.Time `mapstructure:"time"` 74 | } 75 | 76 | type NestedPointerWithMapstructure struct { 77 | Vbar *BasicMapStructure `mapstructure:"vbar"` 78 | } 79 | 80 | type EmbeddedPointerSquashWithNestedMapstructure struct { 81 | *NestedPointerWithMapstructure `mapstructure:",squash"` 82 | Vunique string 83 | } 84 | 85 | type EmbeddedAndNamed struct { 86 | Basic 87 | Named Basic 88 | Vunique string 89 | } 90 | 91 | type SliceAlias []string 92 | 93 | type EmbeddedSlice struct { 94 | SliceAlias `mapstructure:"slice_alias"` 95 | Vunique string 96 | } 97 | 98 | type ArrayAlias [2]string 99 | 100 | type EmbeddedArray struct { 101 | ArrayAlias `mapstructure:"array_alias"` 102 | Vunique string 103 | } 104 | 105 | type SquashOnNonStructType struct { 106 | InvalidSquashType int `mapstructure:",squash"` 107 | } 108 | 109 | type Map struct { 110 | Vfoo string 111 | Vother map[string]string 112 | } 113 | 114 | type MapOfStruct struct { 115 | Value map[string]Basic 116 | } 117 | 118 | type Nested struct { 119 | Vfoo string 120 | Vbar Basic 121 | } 122 | 123 | type NestedPointer struct { 124 | Vfoo string 125 | Vbar *Basic 126 | } 127 | 128 | type NilInterface struct { 129 | W io.Writer 130 | } 131 | 132 | type NilPointer struct { 133 | Value *string 134 | } 135 | 136 | type Slice struct { 137 | Vfoo string 138 | Vbar []string 139 | } 140 | 141 | type SliceOfByte struct { 142 | Vfoo string 143 | Vbar []byte 144 | } 145 | 146 | type SliceOfAlias struct { 147 | Vfoo string 148 | Vbar SliceAlias 149 | } 150 | 151 | type SliceOfStruct struct { 152 | Value []Basic 153 | } 154 | 155 | type SlicePointer struct { 156 | Vbar *[]string 157 | } 158 | 159 | type Array struct { 160 | Vfoo string 161 | Vbar [2]string 162 | } 163 | 164 | type ArrayOfStruct struct { 165 | Value [2]Basic 166 | } 167 | 168 | type Func struct { 169 | Foo func() string 170 | } 171 | 172 | type Tagged struct { 173 | Extra string `mapstructure:"bar,what,what"` 174 | Value string `mapstructure:"foo"` 175 | } 176 | 177 | type Remainder struct { 178 | A string 179 | Extra map[string]interface{} `mapstructure:",remain"` 180 | } 181 | 182 | type StructWithOmitEmpty struct { 183 | VisibleStringField string `mapstructure:"visible-string"` 184 | OmitStringField string `mapstructure:"omittable-string,omitempty"` 185 | VisibleIntField int `mapstructure:"visible-int"` 186 | OmitIntField int `mapstructure:"omittable-int,omitempty"` 187 | VisibleFloatField float64 `mapstructure:"visible-float"` 188 | OmitFloatField float64 `mapstructure:"omittable-float,omitempty"` 189 | VisibleSliceField []interface{} `mapstructure:"visible-slice"` 190 | OmitSliceField []interface{} `mapstructure:"omittable-slice,omitempty"` 191 | VisibleMapField map[string]interface{} `mapstructure:"visible-map"` 192 | OmitMapField map[string]interface{} `mapstructure:"omittable-map,omitempty"` 193 | NestedField *Nested `mapstructure:"visible-nested"` 194 | OmitNestedField *Nested `mapstructure:"omittable-nested,omitempty"` 195 | } 196 | 197 | type TypeConversionResult struct { 198 | IntToFloat float32 199 | IntToUint uint 200 | IntToBool bool 201 | IntToString string 202 | UintToInt int 203 | UintToFloat float32 204 | UintToBool bool 205 | UintToString string 206 | BoolToInt int 207 | BoolToUint uint 208 | BoolToFloat float32 209 | BoolToString string 210 | FloatToInt int 211 | FloatToUint uint 212 | FloatToBool bool 213 | FloatToString string 214 | SliceUint8ToString string 215 | StringToSliceUint8 []byte 216 | ArrayUint8ToString string 217 | StringToInt int 218 | StringToUint uint 219 | StringToBool bool 220 | StringToFloat float32 221 | StringToStrSlice []string 222 | StringToIntSlice []int 223 | StringToStrArray [1]string 224 | StringToIntArray [1]int 225 | SliceToMap map[string]interface{} 226 | MapToSlice []interface{} 227 | ArrayToMap map[string]interface{} 228 | MapToArray [1]interface{} 229 | } 230 | 231 | func TestBasicTypes(t *testing.T) { 232 | t.Parallel() 233 | 234 | input := map[string]interface{}{ 235 | "vstring": "foo", 236 | "vint": 42, 237 | "vint8": 42, 238 | "vint16": 42, 239 | "vint32": 42, 240 | "vint64": 42, 241 | "Vuint": 42, 242 | "vbool": true, 243 | "Vfloat": 42.42, 244 | "vsilent": true, 245 | "vdata": 42, 246 | "vjsonInt": json.Number("1234"), 247 | "vjsonUint": json.Number("1234"), 248 | "vjsonUint64": json.Number("9223372036854775809"), // 2^63 + 1 249 | "vjsonFloat": json.Number("1234.5"), 250 | "vjsonNumber": json.Number("1234.5"), 251 | } 252 | 253 | var result Basic 254 | err := Decode(input, &result) 255 | if err != nil { 256 | t.Errorf("got an err: %s", err.Error()) 257 | t.FailNow() 258 | } 259 | 260 | if result.Vstring != "foo" { 261 | t.Errorf("vstring value should be 'foo': %#v", result.Vstring) 262 | } 263 | 264 | if result.Vint != 42 { 265 | t.Errorf("vint value should be 42: %#v", result.Vint) 266 | } 267 | if result.Vint8 != 42 { 268 | t.Errorf("vint8 value should be 42: %#v", result.Vint) 269 | } 270 | if result.Vint16 != 42 { 271 | t.Errorf("vint16 value should be 42: %#v", result.Vint) 272 | } 273 | if result.Vint32 != 42 { 274 | t.Errorf("vint32 value should be 42: %#v", result.Vint) 275 | } 276 | if result.Vint64 != 42 { 277 | t.Errorf("vint64 value should be 42: %#v", result.Vint) 278 | } 279 | 280 | if result.Vuint != 42 { 281 | t.Errorf("vuint value should be 42: %#v", result.Vuint) 282 | } 283 | 284 | if result.Vbool != true { 285 | t.Errorf("vbool value should be true: %#v", result.Vbool) 286 | } 287 | 288 | if result.Vfloat != 42.42 { 289 | t.Errorf("vfloat value should be 42.42: %#v", result.Vfloat) 290 | } 291 | 292 | if result.Vextra != "" { 293 | t.Errorf("vextra value should be empty: %#v", result.Vextra) 294 | } 295 | 296 | if result.vsilent != false { 297 | t.Error("vsilent should not be set, it is unexported") 298 | } 299 | 300 | if result.Vdata != 42 { 301 | t.Error("vdata should be valid") 302 | } 303 | 304 | if result.VjsonInt != 1234 { 305 | t.Errorf("vjsonint value should be 1234: %#v", result.VjsonInt) 306 | } 307 | 308 | if result.VjsonUint != 1234 { 309 | t.Errorf("vjsonuint value should be 1234: %#v", result.VjsonUint) 310 | } 311 | 312 | if result.VjsonUint64 != 9223372036854775809 { 313 | t.Errorf("vjsonuint64 value should be 9223372036854775809: %#v", result.VjsonUint64) 314 | } 315 | 316 | if result.VjsonFloat != 1234.5 { 317 | t.Errorf("vjsonfloat value should be 1234.5: %#v", result.VjsonFloat) 318 | } 319 | 320 | if !reflect.DeepEqual(result.VjsonNumber, json.Number("1234.5")) { 321 | t.Errorf("vjsonnumber value should be '1234.5': %T, %#v", result.VjsonNumber, result.VjsonNumber) 322 | } 323 | } 324 | 325 | func TestBasic_IntWithFloat(t *testing.T) { 326 | t.Parallel() 327 | 328 | input := map[string]interface{}{ 329 | "vint": float64(42), 330 | } 331 | 332 | var result Basic 333 | err := Decode(input, &result) 334 | if err != nil { 335 | t.Fatalf("got an err: %s", err) 336 | } 337 | } 338 | 339 | func TestBasic_Merge(t *testing.T) { 340 | t.Parallel() 341 | 342 | input := map[string]interface{}{ 343 | "vint": 42, 344 | } 345 | 346 | var result Basic 347 | result.Vuint = 100 348 | err := Decode(input, &result) 349 | if err != nil { 350 | t.Fatalf("got an err: %s", err) 351 | } 352 | 353 | expected := Basic{ 354 | Vint: 42, 355 | Vuint: 100, 356 | } 357 | if !reflect.DeepEqual(result, expected) { 358 | t.Fatalf("bad: %#v", result) 359 | } 360 | } 361 | 362 | // Test for issue #46. 363 | func TestBasic_Struct(t *testing.T) { 364 | t.Parallel() 365 | 366 | input := map[string]interface{}{ 367 | "vdata": map[string]interface{}{ 368 | "vstring": "foo", 369 | }, 370 | } 371 | 372 | var result, inner Basic 373 | result.Vdata = &inner 374 | err := Decode(input, &result) 375 | if err != nil { 376 | t.Fatalf("got an err: %s", err) 377 | } 378 | expected := Basic{ 379 | Vdata: &Basic{ 380 | Vstring: "foo", 381 | }, 382 | } 383 | if !reflect.DeepEqual(result, expected) { 384 | t.Fatalf("bad: %#v", result) 385 | } 386 | } 387 | 388 | func TestBasic_interfaceStruct(t *testing.T) { 389 | t.Parallel() 390 | 391 | input := map[string]interface{}{ 392 | "vstring": "foo", 393 | } 394 | 395 | var iface interface{} = &Basic{} 396 | err := Decode(input, &iface) 397 | if err != nil { 398 | t.Fatalf("got an err: %s", err) 399 | } 400 | 401 | expected := &Basic{ 402 | Vstring: "foo", 403 | } 404 | if !reflect.DeepEqual(iface, expected) { 405 | t.Fatalf("bad: %#v", iface) 406 | } 407 | } 408 | 409 | // Issue 187 410 | func TestBasic_interfaceStructNonPtr(t *testing.T) { 411 | t.Parallel() 412 | 413 | input := map[string]interface{}{ 414 | "vstring": "foo", 415 | } 416 | 417 | var iface interface{} = Basic{} 418 | err := Decode(input, &iface) 419 | if err != nil { 420 | t.Fatalf("got an err: %s", err) 421 | } 422 | 423 | expected := Basic{ 424 | Vstring: "foo", 425 | } 426 | if !reflect.DeepEqual(iface, expected) { 427 | t.Fatalf("bad: %#v", iface) 428 | } 429 | } 430 | 431 | func TestDecode_BasicSquash(t *testing.T) { 432 | t.Parallel() 433 | 434 | input := map[string]interface{}{ 435 | "vstring": "foo", 436 | } 437 | 438 | var result BasicSquash 439 | err := Decode(input, &result) 440 | if err != nil { 441 | t.Fatalf("got an err: %s", err.Error()) 442 | } 443 | 444 | if result.Test.Vstring != "foo" { 445 | t.Errorf("vstring value should be 'foo': %#v", result.Test.Vstring) 446 | } 447 | } 448 | 449 | func TestDecodeFrom_BasicSquash(t *testing.T) { 450 | t.Parallel() 451 | 452 | var v interface{} 453 | var ok bool 454 | 455 | input := BasicSquash{ 456 | Test: Basic{ 457 | Vstring: "foo", 458 | }, 459 | } 460 | 461 | var result map[string]interface{} 462 | err := Decode(input, &result) 463 | if err != nil { 464 | t.Fatalf("got an err: %s", err.Error()) 465 | } 466 | 467 | if _, ok = result["Test"]; ok { 468 | t.Error("test should not be present in map") 469 | } 470 | 471 | v, ok = result["Vstring"] 472 | if !ok { 473 | t.Error("vstring should be present in map") 474 | } else if !reflect.DeepEqual(v, "foo") { 475 | t.Errorf("vstring value should be 'foo': %#v", v) 476 | } 477 | } 478 | 479 | func TestDecode_Embedded(t *testing.T) { 480 | t.Parallel() 481 | 482 | input := map[string]interface{}{ 483 | "vstring": "foo", 484 | "Basic": map[string]interface{}{ 485 | "vstring": "innerfoo", 486 | }, 487 | "vunique": "bar", 488 | } 489 | 490 | var result Embedded 491 | err := Decode(input, &result) 492 | if err != nil { 493 | t.Fatalf("got an err: %s", err.Error()) 494 | } 495 | 496 | if result.Vstring != "innerfoo" { 497 | t.Errorf("vstring value should be 'innerfoo': %#v", result.Vstring) 498 | } 499 | 500 | if result.Vunique != "bar" { 501 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 502 | } 503 | } 504 | 505 | func TestDecode_EmbeddedPointer(t *testing.T) { 506 | t.Parallel() 507 | 508 | input := map[string]interface{}{ 509 | "vstring": "foo", 510 | "Basic": map[string]interface{}{ 511 | "vstring": "innerfoo", 512 | }, 513 | "vunique": "bar", 514 | } 515 | 516 | var result EmbeddedPointer 517 | err := Decode(input, &result) 518 | if err != nil { 519 | t.Fatalf("err: %s", err) 520 | } 521 | 522 | expected := EmbeddedPointer{ 523 | Basic: &Basic{ 524 | Vstring: "innerfoo", 525 | }, 526 | Vunique: "bar", 527 | } 528 | if !reflect.DeepEqual(result, expected) { 529 | t.Fatalf("bad: %#v", result) 530 | } 531 | } 532 | 533 | func TestDecode_EmbeddedSlice(t *testing.T) { 534 | t.Parallel() 535 | 536 | input := map[string]interface{}{ 537 | "slice_alias": []string{"foo", "bar"}, 538 | "vunique": "bar", 539 | } 540 | 541 | var result EmbeddedSlice 542 | err := Decode(input, &result) 543 | if err != nil { 544 | t.Fatalf("got an err: %s", err.Error()) 545 | } 546 | 547 | if !reflect.DeepEqual(result.SliceAlias, SliceAlias([]string{"foo", "bar"})) { 548 | t.Errorf("slice value: %#v", result.SliceAlias) 549 | } 550 | 551 | if result.Vunique != "bar" { 552 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 553 | } 554 | } 555 | 556 | func TestDecode_EmbeddedArray(t *testing.T) { 557 | t.Parallel() 558 | 559 | input := map[string]interface{}{ 560 | "array_alias": [2]string{"foo", "bar"}, 561 | "vunique": "bar", 562 | } 563 | 564 | var result EmbeddedArray 565 | err := Decode(input, &result) 566 | if err != nil { 567 | t.Fatalf("got an err: %s", err.Error()) 568 | } 569 | 570 | if !reflect.DeepEqual(result.ArrayAlias, ArrayAlias([2]string{"foo", "bar"})) { 571 | t.Errorf("array value: %#v", result.ArrayAlias) 572 | } 573 | 574 | if result.Vunique != "bar" { 575 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 576 | } 577 | } 578 | 579 | func TestDecode_decodeSliceWithArray(t *testing.T) { 580 | t.Parallel() 581 | 582 | var result []int 583 | input := [1]int{1} 584 | expected := []int{1} 585 | if err := Decode(input, &result); err != nil { 586 | t.Fatalf("got an err: %s", err.Error()) 587 | } 588 | 589 | if !reflect.DeepEqual(expected, result) { 590 | t.Errorf("wanted %+v, got %+v", expected, result) 591 | } 592 | } 593 | 594 | func TestDecode_EmbeddedNoSquash(t *testing.T) { 595 | t.Parallel() 596 | 597 | input := map[string]interface{}{ 598 | "vstring": "foo", 599 | "vunique": "bar", 600 | } 601 | 602 | var result Embedded 603 | err := Decode(input, &result) 604 | if err != nil { 605 | t.Fatalf("got an err: %s", err.Error()) 606 | } 607 | 608 | if result.Vstring != "" { 609 | t.Errorf("vstring value should be empty: %#v", result.Vstring) 610 | } 611 | 612 | if result.Vunique != "bar" { 613 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 614 | } 615 | } 616 | 617 | func TestDecode_EmbeddedPointerNoSquash(t *testing.T) { 618 | t.Parallel() 619 | 620 | input := map[string]interface{}{ 621 | "vstring": "foo", 622 | "vunique": "bar", 623 | } 624 | 625 | result := EmbeddedPointer{ 626 | Basic: &Basic{}, 627 | } 628 | 629 | err := Decode(input, &result) 630 | if err != nil { 631 | t.Fatalf("err: %s", err) 632 | } 633 | 634 | if result.Vstring != "" { 635 | t.Errorf("vstring value should be empty: %#v", result.Vstring) 636 | } 637 | 638 | if result.Vunique != "bar" { 639 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 640 | } 641 | } 642 | 643 | func TestDecode_EmbeddedSquash(t *testing.T) { 644 | t.Parallel() 645 | 646 | input := map[string]interface{}{ 647 | "vstring": "foo", 648 | "vunique": "bar", 649 | } 650 | 651 | var result EmbeddedSquash 652 | err := Decode(input, &result) 653 | if err != nil { 654 | t.Fatalf("got an err: %s", err.Error()) 655 | } 656 | 657 | if result.Vstring != "foo" { 658 | t.Errorf("vstring value should be 'foo': %#v", result.Vstring) 659 | } 660 | 661 | if result.Vunique != "bar" { 662 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 663 | } 664 | } 665 | 666 | func TestDecodeFrom_EmbeddedSquash(t *testing.T) { 667 | t.Parallel() 668 | 669 | var v interface{} 670 | var ok bool 671 | 672 | input := EmbeddedSquash{ 673 | Basic: Basic{ 674 | Vstring: "foo", 675 | }, 676 | Vunique: "bar", 677 | } 678 | 679 | var result map[string]interface{} 680 | err := Decode(input, &result) 681 | if err != nil { 682 | t.Fatalf("got an err: %s", err.Error()) 683 | } 684 | 685 | if _, ok = result["Basic"]; ok { 686 | t.Error("basic should not be present in map") 687 | } 688 | 689 | v, ok = result["Vstring"] 690 | if !ok { 691 | t.Error("vstring should be present in map") 692 | } else if !reflect.DeepEqual(v, "foo") { 693 | t.Errorf("vstring value should be 'foo': %#v", v) 694 | } 695 | 696 | v, ok = result["Vunique"] 697 | if !ok { 698 | t.Error("vunique should be present in map") 699 | } else if !reflect.DeepEqual(v, "bar") { 700 | t.Errorf("vunique value should be 'bar': %#v", v) 701 | } 702 | } 703 | 704 | func TestDecode_EmbeddedPointerSquash_FromStructToMap(t *testing.T) { 705 | t.Parallel() 706 | 707 | input := EmbeddedPointerSquash{ 708 | Basic: &Basic{ 709 | Vstring: "foo", 710 | }, 711 | Vunique: "bar", 712 | } 713 | 714 | var result map[string]interface{} 715 | err := Decode(input, &result) 716 | if err != nil { 717 | t.Fatalf("got an err: %s", err.Error()) 718 | } 719 | 720 | if result["Vstring"] != "foo" { 721 | t.Errorf("vstring value should be 'foo': %#v", result["Vstring"]) 722 | } 723 | 724 | if result["Vunique"] != "bar" { 725 | t.Errorf("vunique value should be 'bar': %#v", result["Vunique"]) 726 | } 727 | } 728 | 729 | func TestDecode_EmbeddedPointerSquash_FromMapToStruct(t *testing.T) { 730 | t.Parallel() 731 | 732 | input := map[string]interface{}{ 733 | "Vstring": "foo", 734 | "Vunique": "bar", 735 | } 736 | 737 | result := EmbeddedPointerSquash{ 738 | Basic: &Basic{}, 739 | } 740 | err := Decode(input, &result) 741 | if err != nil { 742 | t.Fatalf("got an err: %s", err.Error()) 743 | } 744 | 745 | if result.Vstring != "foo" { 746 | t.Errorf("vstring value should be 'foo': %#v", result.Vstring) 747 | } 748 | 749 | if result.Vunique != "bar" { 750 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 751 | } 752 | } 753 | 754 | func TestDecode_EmbeddedPointerSquashWithNestedMapstructure_FromStructToMap(t *testing.T) { 755 | t.Parallel() 756 | 757 | vTime := time.Now() 758 | 759 | input := EmbeddedPointerSquashWithNestedMapstructure{ 760 | NestedPointerWithMapstructure: &NestedPointerWithMapstructure{ 761 | Vbar: &BasicMapStructure{ 762 | Vunique: "bar", 763 | Vtime: &vTime, 764 | }, 765 | }, 766 | Vunique: "foo", 767 | } 768 | 769 | var result map[string]interface{} 770 | err := Decode(input, &result) 771 | if err != nil { 772 | t.Fatalf("got an err: %s", err.Error()) 773 | } 774 | expected := map[string]interface{}{ 775 | "vbar": map[string]interface{}{ 776 | "vunique": "bar", 777 | "time": &vTime, 778 | }, 779 | "Vunique": "foo", 780 | } 781 | 782 | if !reflect.DeepEqual(result, expected) { 783 | t.Errorf("result should be %#v: got %#v", expected, result) 784 | } 785 | } 786 | 787 | func TestDecode_EmbeddedPointerSquashWithNestedMapstructure_FromMapToStruct(t *testing.T) { 788 | t.Parallel() 789 | 790 | vTime := time.Now() 791 | 792 | input := map[string]interface{}{ 793 | "vbar": map[string]interface{}{ 794 | "vunique": "bar", 795 | "time": &vTime, 796 | }, 797 | "Vunique": "foo", 798 | } 799 | 800 | result := EmbeddedPointerSquashWithNestedMapstructure{ 801 | NestedPointerWithMapstructure: &NestedPointerWithMapstructure{}, 802 | } 803 | err := Decode(input, &result) 804 | if err != nil { 805 | t.Fatalf("got an err: %s", err.Error()) 806 | } 807 | expected := EmbeddedPointerSquashWithNestedMapstructure{ 808 | NestedPointerWithMapstructure: &NestedPointerWithMapstructure{ 809 | Vbar: &BasicMapStructure{ 810 | Vunique: "bar", 811 | Vtime: &vTime, 812 | }, 813 | }, 814 | Vunique: "foo", 815 | } 816 | 817 | if !reflect.DeepEqual(result, expected) { 818 | t.Errorf("result should be %#v: got %#v", expected, result) 819 | } 820 | } 821 | 822 | func TestDecode_EmbeddedSquashConfig(t *testing.T) { 823 | t.Parallel() 824 | 825 | input := map[string]interface{}{ 826 | "vstring": "foo", 827 | "vunique": "bar", 828 | "Named": map[string]interface{}{ 829 | "vstring": "baz", 830 | }, 831 | } 832 | 833 | var result EmbeddedAndNamed 834 | config := &DecoderConfig{ 835 | Squash: true, 836 | Result: &result, 837 | } 838 | 839 | decoder, err := NewDecoder(config) 840 | if err != nil { 841 | t.Fatalf("err: %s", err) 842 | } 843 | 844 | err = decoder.Decode(input) 845 | if err != nil { 846 | t.Fatalf("got an err: %s", err) 847 | } 848 | 849 | if result.Vstring != "foo" { 850 | t.Errorf("vstring value should be 'foo': %#v", result.Vstring) 851 | } 852 | 853 | if result.Vunique != "bar" { 854 | t.Errorf("vunique value should be 'bar': %#v", result.Vunique) 855 | } 856 | 857 | if result.Named.Vstring != "baz" { 858 | t.Errorf("Named.vstring value should be 'baz': %#v", result.Named.Vstring) 859 | } 860 | } 861 | 862 | func TestDecodeFrom_EmbeddedSquashConfig(t *testing.T) { 863 | t.Parallel() 864 | 865 | input := EmbeddedAndNamed{ 866 | Basic: Basic{Vstring: "foo"}, 867 | Named: Basic{Vstring: "baz"}, 868 | Vunique: "bar", 869 | } 870 | 871 | result := map[string]interface{}{} 872 | config := &DecoderConfig{ 873 | Squash: true, 874 | Result: &result, 875 | } 876 | decoder, err := NewDecoder(config) 877 | if err != nil { 878 | t.Fatalf("got an err: %s", err.Error()) 879 | } 880 | 881 | err = decoder.Decode(input) 882 | if err != nil { 883 | t.Fatalf("got an err: %s", err.Error()) 884 | } 885 | 886 | if _, ok := result["Basic"]; ok { 887 | t.Error("basic should not be present in map") 888 | } 889 | 890 | v, ok := result["Vstring"] 891 | if !ok { 892 | t.Error("vstring should be present in map") 893 | } else if !reflect.DeepEqual(v, "foo") { 894 | t.Errorf("vstring value should be 'foo': %#v", v) 895 | } 896 | 897 | v, ok = result["Vunique"] 898 | if !ok { 899 | t.Error("vunique should be present in map") 900 | } else if !reflect.DeepEqual(v, "bar") { 901 | t.Errorf("vunique value should be 'bar': %#v", v) 902 | } 903 | 904 | v, ok = result["Named"] 905 | if !ok { 906 | t.Error("Named should be present in map") 907 | } else { 908 | named := v.(map[string]interface{}) 909 | v, ok := named["Vstring"] 910 | if !ok { 911 | t.Error("Named: vstring should be present in map") 912 | } else if !reflect.DeepEqual(v, "baz") { 913 | t.Errorf("Named: vstring should be 'baz': %#v", v) 914 | } 915 | } 916 | } 917 | 918 | func TestDecodeFrom_EmbeddedSquashConfig_WithTags(t *testing.T) { 919 | t.Parallel() 920 | 921 | var v interface{} 922 | var ok bool 923 | 924 | input := EmbeddedSquash{ 925 | Basic: Basic{ 926 | Vstring: "foo", 927 | }, 928 | Vunique: "bar", 929 | } 930 | 931 | result := map[string]interface{}{} 932 | config := &DecoderConfig{ 933 | Squash: true, 934 | Result: &result, 935 | } 936 | decoder, err := NewDecoder(config) 937 | if err != nil { 938 | t.Fatalf("got an err: %s", err.Error()) 939 | } 940 | 941 | err = decoder.Decode(input) 942 | if err != nil { 943 | t.Fatalf("got an err: %s", err.Error()) 944 | } 945 | 946 | if _, ok = result["Basic"]; ok { 947 | t.Error("basic should not be present in map") 948 | } 949 | 950 | v, ok = result["Vstring"] 951 | if !ok { 952 | t.Error("vstring should be present in map") 953 | } else if !reflect.DeepEqual(v, "foo") { 954 | t.Errorf("vstring value should be 'foo': %#v", v) 955 | } 956 | 957 | v, ok = result["Vunique"] 958 | if !ok { 959 | t.Error("vunique should be present in map") 960 | } else if !reflect.DeepEqual(v, "bar") { 961 | t.Errorf("vunique value should be 'bar': %#v", v) 962 | } 963 | } 964 | 965 | func TestDecode_SquashOnNonStructType(t *testing.T) { 966 | t.Parallel() 967 | 968 | input := map[string]interface{}{ 969 | "InvalidSquashType": 42, 970 | } 971 | 972 | var result SquashOnNonStructType 973 | err := Decode(input, &result) 974 | if err == nil { 975 | t.Fatal("unexpected success decoding invalid squash field type") 976 | } else if !strings.Contains(err.Error(), "unsupported type for squash") { 977 | t.Fatalf("unexpected error message for invalid squash field type: %s", err) 978 | } 979 | } 980 | 981 | func TestDecode_DecodeHook(t *testing.T) { 982 | t.Parallel() 983 | 984 | input := map[string]interface{}{ 985 | "vint": "WHAT", 986 | } 987 | 988 | decodeHook := func(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) { 989 | if from == reflect.String && to != reflect.String { 990 | return 5, nil 991 | } 992 | 993 | return v, nil 994 | } 995 | 996 | var result Basic 997 | config := &DecoderConfig{ 998 | DecodeHook: decodeHook, 999 | Result: &result, 1000 | } 1001 | 1002 | decoder, err := NewDecoder(config) 1003 | if err != nil { 1004 | t.Fatalf("err: %s", err) 1005 | } 1006 | 1007 | err = decoder.Decode(input) 1008 | if err != nil { 1009 | t.Fatalf("got an err: %s", err) 1010 | } 1011 | 1012 | if result.Vint != 5 { 1013 | t.Errorf("vint should be 5: %#v", result.Vint) 1014 | } 1015 | } 1016 | 1017 | func TestDecode_DecodeHookType(t *testing.T) { 1018 | t.Parallel() 1019 | 1020 | input := map[string]interface{}{ 1021 | "vint": "WHAT", 1022 | } 1023 | 1024 | decodeHook := func(from reflect.Type, to reflect.Type, v interface{}) (interface{}, error) { 1025 | if from.Kind() == reflect.String && 1026 | to.Kind() != reflect.String { 1027 | return 5, nil 1028 | } 1029 | 1030 | return v, nil 1031 | } 1032 | 1033 | var result Basic 1034 | config := &DecoderConfig{ 1035 | DecodeHook: decodeHook, 1036 | Result: &result, 1037 | } 1038 | 1039 | decoder, err := NewDecoder(config) 1040 | if err != nil { 1041 | t.Fatalf("err: %s", err) 1042 | } 1043 | 1044 | err = decoder.Decode(input) 1045 | if err != nil { 1046 | t.Fatalf("got an err: %s", err) 1047 | } 1048 | 1049 | if result.Vint != 5 { 1050 | t.Errorf("vint should be 5: %#v", result.Vint) 1051 | } 1052 | } 1053 | 1054 | func TestDecode_Nil(t *testing.T) { 1055 | t.Parallel() 1056 | 1057 | var input interface{} 1058 | result := Basic{ 1059 | Vstring: "foo", 1060 | } 1061 | 1062 | err := Decode(input, &result) 1063 | if err != nil { 1064 | t.Fatalf("err: %s", err) 1065 | } 1066 | 1067 | if result.Vstring != "foo" { 1068 | t.Fatalf("bad: %#v", result.Vstring) 1069 | } 1070 | } 1071 | 1072 | func TestDecode_NilInterfaceHook(t *testing.T) { 1073 | t.Parallel() 1074 | 1075 | input := map[string]interface{}{ 1076 | "w": "", 1077 | } 1078 | 1079 | decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) { 1080 | if t.String() == "io.Writer" { 1081 | return nil, nil 1082 | } 1083 | 1084 | return v, nil 1085 | } 1086 | 1087 | var result NilInterface 1088 | config := &DecoderConfig{ 1089 | DecodeHook: decodeHook, 1090 | Result: &result, 1091 | } 1092 | 1093 | decoder, err := NewDecoder(config) 1094 | if err != nil { 1095 | t.Fatalf("err: %s", err) 1096 | } 1097 | 1098 | err = decoder.Decode(input) 1099 | if err != nil { 1100 | t.Fatalf("got an err: %s", err) 1101 | } 1102 | 1103 | if result.W != nil { 1104 | t.Errorf("W should be nil: %#v", result.W) 1105 | } 1106 | } 1107 | 1108 | func TestDecode_NilPointerHook(t *testing.T) { 1109 | t.Parallel() 1110 | 1111 | input := map[string]interface{}{ 1112 | "value": "", 1113 | } 1114 | 1115 | decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) { 1116 | if typed, ok := v.(string); ok { 1117 | if typed == "" { 1118 | return nil, nil 1119 | } 1120 | } 1121 | return v, nil 1122 | } 1123 | 1124 | var result NilPointer 1125 | config := &DecoderConfig{ 1126 | DecodeHook: decodeHook, 1127 | Result: &result, 1128 | } 1129 | 1130 | decoder, err := NewDecoder(config) 1131 | if err != nil { 1132 | t.Fatalf("err: %s", err) 1133 | } 1134 | 1135 | err = decoder.Decode(input) 1136 | if err != nil { 1137 | t.Fatalf("got an err: %s", err) 1138 | } 1139 | 1140 | if result.Value != nil { 1141 | t.Errorf("W should be nil: %#v", result.Value) 1142 | } 1143 | } 1144 | 1145 | func TestDecode_FuncHook(t *testing.T) { 1146 | t.Parallel() 1147 | 1148 | input := map[string]interface{}{ 1149 | "foo": "baz", 1150 | } 1151 | 1152 | decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) { 1153 | if t.Kind() != reflect.Func { 1154 | return v, nil 1155 | } 1156 | val := v.(string) 1157 | return func() string { return val }, nil 1158 | } 1159 | 1160 | var result Func 1161 | config := &DecoderConfig{ 1162 | DecodeHook: decodeHook, 1163 | Result: &result, 1164 | } 1165 | 1166 | decoder, err := NewDecoder(config) 1167 | if err != nil { 1168 | t.Fatalf("err: %s", err) 1169 | } 1170 | 1171 | err = decoder.Decode(input) 1172 | if err != nil { 1173 | t.Fatalf("got an err: %s", err) 1174 | } 1175 | 1176 | if result.Foo() != "baz" { 1177 | t.Errorf("Foo call result should be 'baz': %s", result.Foo()) 1178 | } 1179 | } 1180 | 1181 | func TestDecode_NonStruct(t *testing.T) { 1182 | t.Parallel() 1183 | 1184 | input := map[string]interface{}{ 1185 | "foo": "bar", 1186 | "bar": "baz", 1187 | } 1188 | 1189 | var result map[string]string 1190 | err := Decode(input, &result) 1191 | if err != nil { 1192 | t.Fatalf("err: %s", err) 1193 | } 1194 | 1195 | if result["foo"] != "bar" { 1196 | t.Fatal("foo is not bar") 1197 | } 1198 | } 1199 | 1200 | func TestDecode_StructMatch(t *testing.T) { 1201 | t.Parallel() 1202 | 1203 | input := map[string]interface{}{ 1204 | "vbar": Basic{ 1205 | Vstring: "foo", 1206 | }, 1207 | } 1208 | 1209 | var result Nested 1210 | err := Decode(input, &result) 1211 | if err != nil { 1212 | t.Fatalf("got an err: %s", err.Error()) 1213 | } 1214 | 1215 | if result.Vbar.Vstring != "foo" { 1216 | t.Errorf("bad: %#v", result) 1217 | } 1218 | } 1219 | 1220 | func TestDecode_TypeConversion(t *testing.T) { 1221 | input := map[string]interface{}{ 1222 | "IntToFloat": 42, 1223 | "IntToUint": 42, 1224 | "IntToBool": 1, 1225 | "IntToString": 42, 1226 | "UintToInt": 42, 1227 | "UintToFloat": 42, 1228 | "UintToBool": 42, 1229 | "UintToString": 42, 1230 | "BoolToInt": true, 1231 | "BoolToUint": true, 1232 | "BoolToFloat": true, 1233 | "BoolToString": true, 1234 | "FloatToInt": 42.42, 1235 | "FloatToUint": 42.42, 1236 | "FloatToBool": 42.42, 1237 | "FloatToString": 42.42, 1238 | "SliceUint8ToString": []uint8("foo"), 1239 | "StringToSliceUint8": "foo", 1240 | "ArrayUint8ToString": [3]uint8{'f', 'o', 'o'}, 1241 | "StringToInt": "42", 1242 | "StringToUint": "42", 1243 | "StringToBool": "1", 1244 | "StringToFloat": "42.42", 1245 | "StringToStrSlice": "A", 1246 | "StringToIntSlice": "42", 1247 | "StringToStrArray": "A", 1248 | "StringToIntArray": "42", 1249 | "SliceToMap": []interface{}{}, 1250 | "MapToSlice": map[string]interface{}{}, 1251 | "ArrayToMap": []interface{}{}, 1252 | "MapToArray": map[string]interface{}{}, 1253 | } 1254 | 1255 | expectedResultStrict := TypeConversionResult{ 1256 | IntToFloat: 42.0, 1257 | IntToUint: 42, 1258 | UintToInt: 42, 1259 | UintToFloat: 42, 1260 | BoolToInt: 0, 1261 | BoolToUint: 0, 1262 | BoolToFloat: 0, 1263 | FloatToInt: 42, 1264 | FloatToUint: 42, 1265 | } 1266 | 1267 | expectedResultWeak := TypeConversionResult{ 1268 | IntToFloat: 42.0, 1269 | IntToUint: 42, 1270 | IntToBool: true, 1271 | IntToString: "42", 1272 | UintToInt: 42, 1273 | UintToFloat: 42, 1274 | UintToBool: true, 1275 | UintToString: "42", 1276 | BoolToInt: 1, 1277 | BoolToUint: 1, 1278 | BoolToFloat: 1, 1279 | BoolToString: "1", 1280 | FloatToInt: 42, 1281 | FloatToUint: 42, 1282 | FloatToBool: true, 1283 | FloatToString: "42.42", 1284 | SliceUint8ToString: "foo", 1285 | StringToSliceUint8: []byte("foo"), 1286 | ArrayUint8ToString: "foo", 1287 | StringToInt: 42, 1288 | StringToUint: 42, 1289 | StringToBool: true, 1290 | StringToFloat: 42.42, 1291 | StringToStrSlice: []string{"A"}, 1292 | StringToIntSlice: []int{42}, 1293 | StringToStrArray: [1]string{"A"}, 1294 | StringToIntArray: [1]int{42}, 1295 | SliceToMap: map[string]interface{}{}, 1296 | MapToSlice: []interface{}{}, 1297 | ArrayToMap: map[string]interface{}{}, 1298 | MapToArray: [1]interface{}{}, 1299 | } 1300 | 1301 | // Test strict type conversion 1302 | var resultStrict TypeConversionResult 1303 | err := Decode(input, &resultStrict) 1304 | if err == nil { 1305 | t.Errorf("should return an error") 1306 | } 1307 | if !reflect.DeepEqual(resultStrict, expectedResultStrict) { 1308 | t.Errorf("expected %v, got: %v", expectedResultStrict, resultStrict) 1309 | } 1310 | 1311 | // Test weak type conversion 1312 | var decoder *Decoder 1313 | var resultWeak TypeConversionResult 1314 | 1315 | config := &DecoderConfig{ 1316 | WeaklyTypedInput: true, 1317 | Result: &resultWeak, 1318 | } 1319 | 1320 | decoder, err = NewDecoder(config) 1321 | if err != nil { 1322 | t.Fatalf("err: %s", err) 1323 | } 1324 | 1325 | err = decoder.Decode(input) 1326 | if err != nil { 1327 | t.Fatalf("got an err: %s", err) 1328 | } 1329 | 1330 | if !reflect.DeepEqual(resultWeak, expectedResultWeak) { 1331 | t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak) 1332 | } 1333 | } 1334 | 1335 | func TestDecoder_ErrorUnused(t *testing.T) { 1336 | t.Parallel() 1337 | 1338 | input := map[string]interface{}{ 1339 | "vstring": "hello", 1340 | "foo": "bar", 1341 | } 1342 | 1343 | var result Basic 1344 | config := &DecoderConfig{ 1345 | ErrorUnused: true, 1346 | Result: &result, 1347 | } 1348 | 1349 | decoder, err := NewDecoder(config) 1350 | if err != nil { 1351 | t.Fatalf("err: %s", err) 1352 | } 1353 | 1354 | err = decoder.Decode(input) 1355 | if err == nil { 1356 | t.Fatal("expected error") 1357 | } 1358 | } 1359 | 1360 | func TestDecoder_ErrorUnused_NotSetable(t *testing.T) { 1361 | t.Parallel() 1362 | 1363 | // lowercase vsilent is unexported and cannot be set 1364 | input := map[string]interface{}{ 1365 | "vsilent": "false", 1366 | } 1367 | 1368 | var result Basic 1369 | config := &DecoderConfig{ 1370 | ErrorUnused: true, 1371 | Result: &result, 1372 | } 1373 | 1374 | decoder, err := NewDecoder(config) 1375 | if err != nil { 1376 | t.Fatalf("err: %s", err) 1377 | } 1378 | 1379 | err = decoder.Decode(input) 1380 | if err == nil { 1381 | t.Fatal("expected error") 1382 | } 1383 | } 1384 | func TestDecoder_ErrorUnset(t *testing.T) { 1385 | t.Parallel() 1386 | 1387 | input := map[string]interface{}{ 1388 | "vstring": "hello", 1389 | "foo": "bar", 1390 | } 1391 | 1392 | var result Basic 1393 | config := &DecoderConfig{ 1394 | ErrorUnset: true, 1395 | Result: &result, 1396 | } 1397 | 1398 | decoder, err := NewDecoder(config) 1399 | if err != nil { 1400 | t.Fatalf("err: %s", err) 1401 | } 1402 | 1403 | err = decoder.Decode(input) 1404 | if err == nil { 1405 | t.Fatal("expected error") 1406 | } 1407 | } 1408 | 1409 | func TestMap(t *testing.T) { 1410 | t.Parallel() 1411 | 1412 | input := map[string]interface{}{ 1413 | "vfoo": "foo", 1414 | "vother": map[interface{}]interface{}{ 1415 | "foo": "foo", 1416 | "bar": "bar", 1417 | }, 1418 | } 1419 | 1420 | var result Map 1421 | err := Decode(input, &result) 1422 | if err != nil { 1423 | t.Fatalf("got an error: %s", err) 1424 | } 1425 | 1426 | if result.Vfoo != "foo" { 1427 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 1428 | } 1429 | 1430 | if result.Vother == nil { 1431 | t.Fatal("vother should not be nil") 1432 | } 1433 | 1434 | if len(result.Vother) != 2 { 1435 | t.Error("vother should have two items") 1436 | } 1437 | 1438 | if result.Vother["foo"] != "foo" { 1439 | t.Errorf("'foo' key should be foo, got: %#v", result.Vother["foo"]) 1440 | } 1441 | 1442 | if result.Vother["bar"] != "bar" { 1443 | t.Errorf("'bar' key should be bar, got: %#v", result.Vother["bar"]) 1444 | } 1445 | } 1446 | 1447 | func TestMapMerge(t *testing.T) { 1448 | t.Parallel() 1449 | 1450 | input := map[string]interface{}{ 1451 | "vfoo": "foo", 1452 | "vother": map[interface{}]interface{}{ 1453 | "foo": "foo", 1454 | "bar": "bar", 1455 | }, 1456 | } 1457 | 1458 | var result Map 1459 | result.Vother = map[string]string{"hello": "world"} 1460 | err := Decode(input, &result) 1461 | if err != nil { 1462 | t.Fatalf("got an error: %s", err) 1463 | } 1464 | 1465 | if result.Vfoo != "foo" { 1466 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 1467 | } 1468 | 1469 | expected := map[string]string{ 1470 | "foo": "foo", 1471 | "bar": "bar", 1472 | "hello": "world", 1473 | } 1474 | if !reflect.DeepEqual(result.Vother, expected) { 1475 | t.Errorf("bad: %#v", result.Vother) 1476 | } 1477 | } 1478 | 1479 | func TestMapOfStruct(t *testing.T) { 1480 | t.Parallel() 1481 | 1482 | input := map[string]interface{}{ 1483 | "value": map[string]interface{}{ 1484 | "foo": map[string]string{"vstring": "one"}, 1485 | "bar": map[string]string{"vstring": "two"}, 1486 | }, 1487 | } 1488 | 1489 | var result MapOfStruct 1490 | err := Decode(input, &result) 1491 | if err != nil { 1492 | t.Fatalf("got an err: %s", err) 1493 | } 1494 | 1495 | if result.Value == nil { 1496 | t.Fatal("value should not be nil") 1497 | } 1498 | 1499 | if len(result.Value) != 2 { 1500 | t.Error("value should have two items") 1501 | } 1502 | 1503 | if result.Value["foo"].Vstring != "one" { 1504 | t.Errorf("foo value should be 'one', got: %s", result.Value["foo"].Vstring) 1505 | } 1506 | 1507 | if result.Value["bar"].Vstring != "two" { 1508 | t.Errorf("bar value should be 'two', got: %s", result.Value["bar"].Vstring) 1509 | } 1510 | } 1511 | 1512 | func TestNestedType(t *testing.T) { 1513 | t.Parallel() 1514 | 1515 | input := map[string]interface{}{ 1516 | "vfoo": "foo", 1517 | "vbar": map[string]interface{}{ 1518 | "vstring": "foo", 1519 | "vint": 42, 1520 | "vbool": true, 1521 | }, 1522 | } 1523 | 1524 | var result Nested 1525 | err := Decode(input, &result) 1526 | if err != nil { 1527 | t.Fatalf("got an err: %s", err.Error()) 1528 | } 1529 | 1530 | if result.Vfoo != "foo" { 1531 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 1532 | } 1533 | 1534 | if result.Vbar.Vstring != "foo" { 1535 | t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring) 1536 | } 1537 | 1538 | if result.Vbar.Vint != 42 { 1539 | t.Errorf("vint value should be 42: %#v", result.Vbar.Vint) 1540 | } 1541 | 1542 | if result.Vbar.Vbool != true { 1543 | t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool) 1544 | } 1545 | 1546 | if result.Vbar.Vextra != "" { 1547 | t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra) 1548 | } 1549 | } 1550 | 1551 | func TestNestedTypePointer(t *testing.T) { 1552 | t.Parallel() 1553 | 1554 | input := map[string]interface{}{ 1555 | "vfoo": "foo", 1556 | "vbar": &map[string]interface{}{ 1557 | "vstring": "foo", 1558 | "vint": 42, 1559 | "vbool": true, 1560 | }, 1561 | } 1562 | 1563 | var result NestedPointer 1564 | err := Decode(input, &result) 1565 | if err != nil { 1566 | t.Fatalf("got an err: %s", err.Error()) 1567 | } 1568 | 1569 | if result.Vfoo != "foo" { 1570 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 1571 | } 1572 | 1573 | if result.Vbar.Vstring != "foo" { 1574 | t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring) 1575 | } 1576 | 1577 | if result.Vbar.Vint != 42 { 1578 | t.Errorf("vint value should be 42: %#v", result.Vbar.Vint) 1579 | } 1580 | 1581 | if result.Vbar.Vbool != true { 1582 | t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool) 1583 | } 1584 | 1585 | if result.Vbar.Vextra != "" { 1586 | t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra) 1587 | } 1588 | } 1589 | 1590 | // Test for issue #46. 1591 | func TestNestedTypeInterface(t *testing.T) { 1592 | t.Parallel() 1593 | 1594 | input := map[string]interface{}{ 1595 | "vfoo": "foo", 1596 | "vbar": &map[string]interface{}{ 1597 | "vstring": "foo", 1598 | "vint": 42, 1599 | "vbool": true, 1600 | 1601 | "vdata": map[string]interface{}{ 1602 | "vstring": "bar", 1603 | }, 1604 | }, 1605 | } 1606 | 1607 | var result NestedPointer 1608 | result.Vbar = new(Basic) 1609 | result.Vbar.Vdata = new(Basic) 1610 | err := Decode(input, &result) 1611 | if err != nil { 1612 | t.Fatalf("got an err: %s", err.Error()) 1613 | } 1614 | 1615 | if result.Vfoo != "foo" { 1616 | t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo) 1617 | } 1618 | 1619 | if result.Vbar.Vstring != "foo" { 1620 | t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring) 1621 | } 1622 | 1623 | if result.Vbar.Vint != 42 { 1624 | t.Errorf("vint value should be 42: %#v", result.Vbar.Vint) 1625 | } 1626 | 1627 | if result.Vbar.Vbool != true { 1628 | t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool) 1629 | } 1630 | 1631 | if result.Vbar.Vextra != "" { 1632 | t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra) 1633 | } 1634 | 1635 | if result.Vbar.Vdata.(*Basic).Vstring != "bar" { 1636 | t.Errorf("vstring value should be 'bar': %#v", result.Vbar.Vdata.(*Basic).Vstring) 1637 | } 1638 | } 1639 | 1640 | func TestSlice(t *testing.T) { 1641 | t.Parallel() 1642 | 1643 | inputStringSlice := map[string]interface{}{ 1644 | "vfoo": "foo", 1645 | "vbar": []string{"foo", "bar", "baz"}, 1646 | } 1647 | 1648 | inputStringSlicePointer := map[string]interface{}{ 1649 | "vfoo": "foo", 1650 | "vbar": &[]string{"foo", "bar", "baz"}, 1651 | } 1652 | 1653 | outputStringSlice := &Slice{ 1654 | "foo", 1655 | []string{"foo", "bar", "baz"}, 1656 | } 1657 | 1658 | testSliceInput(t, inputStringSlice, outputStringSlice) 1659 | testSliceInput(t, inputStringSlicePointer, outputStringSlice) 1660 | } 1661 | 1662 | func TestNotEmptyByteSlice(t *testing.T) { 1663 | t.Parallel() 1664 | 1665 | inputByteSlice := map[string]interface{}{ 1666 | "vfoo": "foo", 1667 | "vbar": []byte(`{"bar": "bar"}`), 1668 | } 1669 | 1670 | result := SliceOfByte{ 1671 | Vfoo: "another foo", 1672 | Vbar: []byte(`{"bar": "bar bar bar bar bar bar bar bar"}`), 1673 | } 1674 | 1675 | err := Decode(inputByteSlice, &result) 1676 | if err != nil { 1677 | t.Fatalf("got unexpected error: %s", err) 1678 | } 1679 | 1680 | expected := SliceOfByte{ 1681 | Vfoo: "foo", 1682 | Vbar: []byte(`{"bar": "bar"}`), 1683 | } 1684 | 1685 | if !reflect.DeepEqual(result, expected) { 1686 | t.Errorf("bad: %#v", result) 1687 | } 1688 | } 1689 | 1690 | func TestInvalidSlice(t *testing.T) { 1691 | t.Parallel() 1692 | 1693 | input := map[string]interface{}{ 1694 | "vfoo": "foo", 1695 | "vbar": 42, 1696 | } 1697 | 1698 | result := Slice{} 1699 | err := Decode(input, &result) 1700 | if err == nil { 1701 | t.Errorf("expected failure") 1702 | } 1703 | } 1704 | 1705 | func TestSliceOfStruct(t *testing.T) { 1706 | t.Parallel() 1707 | 1708 | input := map[string]interface{}{ 1709 | "value": []map[string]interface{}{ 1710 | {"vstring": "one"}, 1711 | {"vstring": "two"}, 1712 | }, 1713 | } 1714 | 1715 | var result SliceOfStruct 1716 | err := Decode(input, &result) 1717 | if err != nil { 1718 | t.Fatalf("got unexpected error: %s", err) 1719 | } 1720 | 1721 | if len(result.Value) != 2 { 1722 | t.Fatalf("expected two values, got %d", len(result.Value)) 1723 | } 1724 | 1725 | if result.Value[0].Vstring != "one" { 1726 | t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring) 1727 | } 1728 | 1729 | if result.Value[1].Vstring != "two" { 1730 | t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring) 1731 | } 1732 | } 1733 | 1734 | func TestSliceCornerCases(t *testing.T) { 1735 | t.Parallel() 1736 | 1737 | // Input with a map with zero values 1738 | input := map[string]interface{}{} 1739 | var resultWeak []Basic 1740 | 1741 | err := WeakDecode(input, &resultWeak) 1742 | if err != nil { 1743 | t.Fatalf("got unexpected error: %s", err) 1744 | } 1745 | 1746 | if len(resultWeak) != 0 { 1747 | t.Errorf("length should be 0") 1748 | } 1749 | // Input with more values 1750 | input = map[string]interface{}{ 1751 | "Vstring": "foo", 1752 | } 1753 | 1754 | resultWeak = nil 1755 | err = WeakDecode(input, &resultWeak) 1756 | if err != nil { 1757 | t.Fatalf("got unexpected error: %s", err) 1758 | } 1759 | 1760 | if resultWeak[0].Vstring != "foo" { 1761 | t.Errorf("value does not match") 1762 | } 1763 | } 1764 | 1765 | func TestSliceToMap(t *testing.T) { 1766 | t.Parallel() 1767 | 1768 | input := []map[string]interface{}{ 1769 | { 1770 | "foo": "bar", 1771 | }, 1772 | { 1773 | "bar": "baz", 1774 | }, 1775 | } 1776 | 1777 | var result map[string]interface{} 1778 | err := WeakDecode(input, &result) 1779 | if err != nil { 1780 | t.Fatalf("got an error: %s", err) 1781 | } 1782 | 1783 | expected := map[string]interface{}{ 1784 | "foo": "bar", 1785 | "bar": "baz", 1786 | } 1787 | if !reflect.DeepEqual(result, expected) { 1788 | t.Errorf("bad: %#v", result) 1789 | } 1790 | } 1791 | 1792 | func TestArray(t *testing.T) { 1793 | t.Parallel() 1794 | 1795 | inputStringArray := map[string]interface{}{ 1796 | "vfoo": "foo", 1797 | "vbar": [2]string{"foo", "bar"}, 1798 | } 1799 | 1800 | inputStringArrayPointer := map[string]interface{}{ 1801 | "vfoo": "foo", 1802 | "vbar": &[2]string{"foo", "bar"}, 1803 | } 1804 | 1805 | outputStringArray := &Array{ 1806 | "foo", 1807 | [2]string{"foo", "bar"}, 1808 | } 1809 | 1810 | testArrayInput(t, inputStringArray, outputStringArray) 1811 | testArrayInput(t, inputStringArrayPointer, outputStringArray) 1812 | } 1813 | 1814 | func TestInvalidArray(t *testing.T) { 1815 | t.Parallel() 1816 | 1817 | input := map[string]interface{}{ 1818 | "vfoo": "foo", 1819 | "vbar": 42, 1820 | } 1821 | 1822 | result := Array{} 1823 | err := Decode(input, &result) 1824 | if err == nil { 1825 | t.Errorf("expected failure") 1826 | } 1827 | } 1828 | 1829 | func TestArrayOfStruct(t *testing.T) { 1830 | t.Parallel() 1831 | 1832 | input := map[string]interface{}{ 1833 | "value": []map[string]interface{}{ 1834 | {"vstring": "one"}, 1835 | {"vstring": "two"}, 1836 | }, 1837 | } 1838 | 1839 | var result ArrayOfStruct 1840 | err := Decode(input, &result) 1841 | if err != nil { 1842 | t.Fatalf("got unexpected error: %s", err) 1843 | } 1844 | 1845 | if len(result.Value) != 2 { 1846 | t.Fatalf("expected two values, got %d", len(result.Value)) 1847 | } 1848 | 1849 | if result.Value[0].Vstring != "one" { 1850 | t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring) 1851 | } 1852 | 1853 | if result.Value[1].Vstring != "two" { 1854 | t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring) 1855 | } 1856 | } 1857 | 1858 | func TestArrayToMap(t *testing.T) { 1859 | t.Parallel() 1860 | 1861 | input := []map[string]interface{}{ 1862 | { 1863 | "foo": "bar", 1864 | }, 1865 | { 1866 | "bar": "baz", 1867 | }, 1868 | } 1869 | 1870 | var result map[string]interface{} 1871 | err := WeakDecode(input, &result) 1872 | if err != nil { 1873 | t.Fatalf("got an error: %s", err) 1874 | } 1875 | 1876 | expected := map[string]interface{}{ 1877 | "foo": "bar", 1878 | "bar": "baz", 1879 | } 1880 | if !reflect.DeepEqual(result, expected) { 1881 | t.Errorf("bad: %#v", result) 1882 | } 1883 | } 1884 | 1885 | func TestDecodeTable(t *testing.T) { 1886 | t.Parallel() 1887 | 1888 | // We need to make new types so that we don't get the short-circuit 1889 | // copy functionality. We want to test the deep copying functionality. 1890 | type BasicCopy Basic 1891 | type NestedPointerCopy NestedPointer 1892 | type MapCopy Map 1893 | 1894 | tests := []struct { 1895 | name string 1896 | in interface{} 1897 | target interface{} 1898 | out interface{} 1899 | wantErr bool 1900 | }{ 1901 | { 1902 | "basic struct input", 1903 | &Basic{ 1904 | Vstring: "vstring", 1905 | Vint: 2, 1906 | Vint8: 2, 1907 | Vint16: 2, 1908 | Vint32: 2, 1909 | Vint64: 2, 1910 | Vuint: 3, 1911 | Vbool: true, 1912 | Vfloat: 4.56, 1913 | Vextra: "vextra", 1914 | vsilent: true, 1915 | Vdata: []byte("data"), 1916 | }, 1917 | &map[string]interface{}{}, 1918 | &map[string]interface{}{ 1919 | "Vstring": "vstring", 1920 | "Vint": 2, 1921 | "Vint8": int8(2), 1922 | "Vint16": int16(2), 1923 | "Vint32": int32(2), 1924 | "Vint64": int64(2), 1925 | "Vuint": uint(3), 1926 | "Vbool": true, 1927 | "Vfloat": 4.56, 1928 | "Vextra": "vextra", 1929 | "Vdata": []byte("data"), 1930 | "VjsonInt": 0, 1931 | "VjsonUint": uint(0), 1932 | "VjsonUint64": uint64(0), 1933 | "VjsonFloat": 0.0, 1934 | "VjsonNumber": json.Number(""), 1935 | }, 1936 | false, 1937 | }, 1938 | { 1939 | "embedded struct input", 1940 | &Embedded{ 1941 | Vunique: "vunique", 1942 | Basic: Basic{ 1943 | Vstring: "vstring", 1944 | Vint: 2, 1945 | Vint8: 2, 1946 | Vint16: 2, 1947 | Vint32: 2, 1948 | Vint64: 2, 1949 | Vuint: 3, 1950 | Vbool: true, 1951 | Vfloat: 4.56, 1952 | Vextra: "vextra", 1953 | vsilent: true, 1954 | Vdata: []byte("data"), 1955 | }, 1956 | }, 1957 | &map[string]interface{}{}, 1958 | &map[string]interface{}{ 1959 | "Vunique": "vunique", 1960 | "Basic": map[string]interface{}{ 1961 | "Vstring": "vstring", 1962 | "Vint": 2, 1963 | "Vint8": int8(2), 1964 | "Vint16": int16(2), 1965 | "Vint32": int32(2), 1966 | "Vint64": int64(2), 1967 | "Vuint": uint(3), 1968 | "Vbool": true, 1969 | "Vfloat": 4.56, 1970 | "Vextra": "vextra", 1971 | "Vdata": []byte("data"), 1972 | "VjsonInt": 0, 1973 | "VjsonUint": uint(0), 1974 | "VjsonUint64": uint64(0), 1975 | "VjsonFloat": 0.0, 1976 | "VjsonNumber": json.Number(""), 1977 | }, 1978 | }, 1979 | false, 1980 | }, 1981 | { 1982 | "struct => struct", 1983 | &Basic{ 1984 | Vstring: "vstring", 1985 | Vint: 2, 1986 | Vuint: 3, 1987 | Vbool: true, 1988 | Vfloat: 4.56, 1989 | Vextra: "vextra", 1990 | Vdata: []byte("data"), 1991 | vsilent: true, 1992 | }, 1993 | &BasicCopy{}, 1994 | &BasicCopy{ 1995 | Vstring: "vstring", 1996 | Vint: 2, 1997 | Vuint: 3, 1998 | Vbool: true, 1999 | Vfloat: 4.56, 2000 | Vextra: "vextra", 2001 | Vdata: []byte("data"), 2002 | }, 2003 | false, 2004 | }, 2005 | { 2006 | "struct => struct with pointers", 2007 | &NestedPointer{ 2008 | Vfoo: "hello", 2009 | Vbar: nil, 2010 | }, 2011 | &NestedPointerCopy{}, 2012 | &NestedPointerCopy{ 2013 | Vfoo: "hello", 2014 | }, 2015 | false, 2016 | }, 2017 | { 2018 | "basic pointer to non-pointer", 2019 | &BasicPointer{ 2020 | Vstring: stringPtr("vstring"), 2021 | Vint: intPtr(2), 2022 | Vuint: uintPtr(3), 2023 | Vbool: boolPtr(true), 2024 | Vfloat: floatPtr(4.56), 2025 | Vdata: interfacePtr([]byte("data")), 2026 | }, 2027 | &Basic{}, 2028 | &Basic{ 2029 | Vstring: "vstring", 2030 | Vint: 2, 2031 | Vuint: 3, 2032 | Vbool: true, 2033 | Vfloat: 4.56, 2034 | Vdata: []byte("data"), 2035 | }, 2036 | false, 2037 | }, 2038 | { 2039 | "slice non-pointer to pointer", 2040 | &Slice{}, 2041 | &SlicePointer{}, 2042 | &SlicePointer{}, 2043 | false, 2044 | }, 2045 | { 2046 | "slice non-pointer to pointer, zero field", 2047 | &Slice{}, 2048 | &SlicePointer{ 2049 | Vbar: &[]string{"yo"}, 2050 | }, 2051 | &SlicePointer{}, 2052 | false, 2053 | }, 2054 | { 2055 | "slice to slice alias", 2056 | &Slice{}, 2057 | &SliceOfAlias{}, 2058 | &SliceOfAlias{}, 2059 | false, 2060 | }, 2061 | { 2062 | "nil map to map", 2063 | &Map{}, 2064 | &MapCopy{}, 2065 | &MapCopy{}, 2066 | false, 2067 | }, 2068 | { 2069 | "nil map to non-empty map", 2070 | &Map{}, 2071 | &MapCopy{Vother: map[string]string{"foo": "bar"}}, 2072 | &MapCopy{}, 2073 | false, 2074 | }, 2075 | 2076 | { 2077 | "slice input - should error", 2078 | []string{"foo", "bar"}, 2079 | &map[string]interface{}{}, 2080 | &map[string]interface{}{}, 2081 | true, 2082 | }, 2083 | { 2084 | "struct with slice property", 2085 | &Slice{ 2086 | Vfoo: "vfoo", 2087 | Vbar: []string{"foo", "bar"}, 2088 | }, 2089 | &map[string]interface{}{}, 2090 | &map[string]interface{}{ 2091 | "Vfoo": "vfoo", 2092 | "Vbar": []string{"foo", "bar"}, 2093 | }, 2094 | false, 2095 | }, 2096 | { 2097 | "struct with empty slice", 2098 | &map[string]interface{}{ 2099 | "Vbar": []string{}, 2100 | }, 2101 | &Slice{}, 2102 | &Slice{ 2103 | Vbar: []string{}, 2104 | }, 2105 | false, 2106 | }, 2107 | { 2108 | "struct with slice of struct property", 2109 | &SliceOfStruct{ 2110 | Value: []Basic{ 2111 | Basic{ 2112 | Vstring: "vstring", 2113 | Vint: 2, 2114 | Vuint: 3, 2115 | Vbool: true, 2116 | Vfloat: 4.56, 2117 | Vextra: "vextra", 2118 | vsilent: true, 2119 | Vdata: []byte("data"), 2120 | }, 2121 | }, 2122 | }, 2123 | &map[string]interface{}{}, 2124 | &map[string]interface{}{ 2125 | "Value": []Basic{ 2126 | Basic{ 2127 | Vstring: "vstring", 2128 | Vint: 2, 2129 | Vuint: 3, 2130 | Vbool: true, 2131 | Vfloat: 4.56, 2132 | Vextra: "vextra", 2133 | vsilent: true, 2134 | Vdata: []byte("data"), 2135 | }, 2136 | }, 2137 | }, 2138 | false, 2139 | }, 2140 | { 2141 | "struct with map property", 2142 | &Map{ 2143 | Vfoo: "vfoo", 2144 | Vother: map[string]string{"vother": "vother"}, 2145 | }, 2146 | &map[string]interface{}{}, 2147 | &map[string]interface{}{ 2148 | "Vfoo": "vfoo", 2149 | "Vother": map[string]string{ 2150 | "vother": "vother", 2151 | }}, 2152 | false, 2153 | }, 2154 | { 2155 | "tagged struct", 2156 | &Tagged{ 2157 | Extra: "extra", 2158 | Value: "value", 2159 | }, 2160 | &map[string]string{}, 2161 | &map[string]string{ 2162 | "bar": "extra", 2163 | "foo": "value", 2164 | }, 2165 | false, 2166 | }, 2167 | { 2168 | "omit tag struct", 2169 | &struct { 2170 | Value string `mapstructure:"value"` 2171 | Omit string `mapstructure:"-"` 2172 | }{ 2173 | Value: "value", 2174 | Omit: "omit", 2175 | }, 2176 | &map[string]string{}, 2177 | &map[string]string{ 2178 | "value": "value", 2179 | }, 2180 | false, 2181 | }, 2182 | { 2183 | "decode to wrong map type", 2184 | &struct { 2185 | Value string 2186 | }{ 2187 | Value: "string", 2188 | }, 2189 | &map[string]int{}, 2190 | &map[string]int{}, 2191 | true, 2192 | }, 2193 | { 2194 | "remainder", 2195 | map[string]interface{}{ 2196 | "A": "hello", 2197 | "B": "goodbye", 2198 | "C": "yo", 2199 | }, 2200 | &Remainder{}, 2201 | &Remainder{ 2202 | A: "hello", 2203 | Extra: map[string]interface{}{ 2204 | "B": "goodbye", 2205 | "C": "yo", 2206 | }, 2207 | }, 2208 | false, 2209 | }, 2210 | { 2211 | "remainder with no extra", 2212 | map[string]interface{}{ 2213 | "A": "hello", 2214 | }, 2215 | &Remainder{}, 2216 | &Remainder{ 2217 | A: "hello", 2218 | Extra: nil, 2219 | }, 2220 | false, 2221 | }, 2222 | { 2223 | "struct with omitempty tag return non-empty values", 2224 | &struct { 2225 | VisibleField interface{} `mapstructure:"visible"` 2226 | OmitField interface{} `mapstructure:"omittable,omitempty"` 2227 | }{ 2228 | VisibleField: nil, 2229 | OmitField: "string", 2230 | }, 2231 | &map[string]interface{}{}, 2232 | &map[string]interface{}{"visible": nil, "omittable": "string"}, 2233 | false, 2234 | }, 2235 | { 2236 | "struct with omitempty tag ignore empty values", 2237 | &struct { 2238 | VisibleField interface{} `mapstructure:"visible"` 2239 | OmitField interface{} `mapstructure:"omittable,omitempty"` 2240 | }{ 2241 | VisibleField: nil, 2242 | OmitField: nil, 2243 | }, 2244 | &map[string]interface{}{}, 2245 | &map[string]interface{}{"visible": nil}, 2246 | false, 2247 | }, 2248 | } 2249 | 2250 | for _, tt := range tests { 2251 | t.Run(tt.name, func(t *testing.T) { 2252 | if err := Decode(tt.in, tt.target); (err != nil) != tt.wantErr { 2253 | t.Fatalf("%q: TestMapOutputForStructuredInputs() unexpected error: %s", tt.name, err) 2254 | } 2255 | 2256 | if !reflect.DeepEqual(tt.out, tt.target) { 2257 | t.Fatalf("%q: TestMapOutputForStructuredInputs() expected: %#v, got: %#v", tt.name, tt.out, tt.target) 2258 | } 2259 | }) 2260 | } 2261 | } 2262 | 2263 | func TestInvalidType(t *testing.T) { 2264 | t.Parallel() 2265 | 2266 | input := map[string]interface{}{ 2267 | "vstring": 42, 2268 | } 2269 | 2270 | var result Basic 2271 | err := Decode(input, &result) 2272 | if err == nil { 2273 | t.Fatal("error should exist") 2274 | } 2275 | 2276 | derr, ok := err.(*Error) 2277 | if !ok { 2278 | t.Fatalf("error should be kind of Error, instead: %#v", err) 2279 | } 2280 | 2281 | if derr.Errors[0] != 2282 | "'Vstring' expected type 'string', got unconvertible type 'int', value: '42'" { 2283 | t.Errorf("got unexpected error: %s", err) 2284 | } 2285 | 2286 | inputNegIntUint := map[string]interface{}{ 2287 | "vuint": -42, 2288 | } 2289 | 2290 | err = Decode(inputNegIntUint, &result) 2291 | if err == nil { 2292 | t.Fatal("error should exist") 2293 | } 2294 | 2295 | derr, ok = err.(*Error) 2296 | if !ok { 2297 | t.Fatalf("error should be kind of Error, instead: %#v", err) 2298 | } 2299 | 2300 | if derr.Errors[0] != "cannot parse 'Vuint', -42 overflows uint" { 2301 | t.Errorf("got unexpected error: %s", err) 2302 | } 2303 | 2304 | inputNegFloatUint := map[string]interface{}{ 2305 | "vuint": -42.0, 2306 | } 2307 | 2308 | err = Decode(inputNegFloatUint, &result) 2309 | if err == nil { 2310 | t.Fatal("error should exist") 2311 | } 2312 | 2313 | derr, ok = err.(*Error) 2314 | if !ok { 2315 | t.Fatalf("error should be kind of Error, instead: %#v", err) 2316 | } 2317 | 2318 | if derr.Errors[0] != "cannot parse 'Vuint', -42.000000 overflows uint" { 2319 | t.Errorf("got unexpected error: %s", err) 2320 | } 2321 | } 2322 | 2323 | func TestDecodeMetadata(t *testing.T) { 2324 | t.Parallel() 2325 | 2326 | input := map[string]interface{}{ 2327 | "vfoo": "foo", 2328 | "vbar": map[string]interface{}{ 2329 | "vstring": "foo", 2330 | "Vuint": 42, 2331 | "vsilent": "false", 2332 | "foo": "bar", 2333 | }, 2334 | "bar": "nil", 2335 | } 2336 | 2337 | var md Metadata 2338 | var result Nested 2339 | 2340 | err := DecodeMetadata(input, &result, &md) 2341 | if err != nil { 2342 | t.Fatalf("err: %s", err.Error()) 2343 | } 2344 | 2345 | expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"} 2346 | sort.Strings(md.Keys) 2347 | if !reflect.DeepEqual(md.Keys, expectedKeys) { 2348 | t.Fatalf("bad keys: %#v", md.Keys) 2349 | } 2350 | 2351 | expectedUnused := []string{"Vbar.foo", "Vbar.vsilent", "bar"} 2352 | sort.Strings(md.Unused) 2353 | if !reflect.DeepEqual(md.Unused, expectedUnused) { 2354 | t.Fatalf("bad unused: %#v", md.Unused) 2355 | } 2356 | } 2357 | 2358 | func TestMetadata(t *testing.T) { 2359 | t.Parallel() 2360 | 2361 | type testResult struct { 2362 | Vfoo string 2363 | Vbar BasicPointer 2364 | } 2365 | 2366 | input := map[string]interface{}{ 2367 | "vfoo": "foo", 2368 | "vbar": map[string]interface{}{ 2369 | "vstring": "foo", 2370 | "Vuint": 42, 2371 | "vsilent": "false", 2372 | "foo": "bar", 2373 | }, 2374 | "bar": "nil", 2375 | } 2376 | 2377 | var md Metadata 2378 | var result testResult 2379 | config := &DecoderConfig{ 2380 | Metadata: &md, 2381 | Result: &result, 2382 | } 2383 | 2384 | decoder, err := NewDecoder(config) 2385 | if err != nil { 2386 | t.Fatalf("err: %s", err) 2387 | } 2388 | 2389 | err = decoder.Decode(input) 2390 | if err != nil { 2391 | t.Fatalf("err: %s", err.Error()) 2392 | } 2393 | 2394 | expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"} 2395 | sort.Strings(md.Keys) 2396 | if !reflect.DeepEqual(md.Keys, expectedKeys) { 2397 | t.Fatalf("bad keys: %#v", md.Keys) 2398 | } 2399 | 2400 | expectedUnused := []string{"Vbar.foo", "Vbar.vsilent", "bar"} 2401 | sort.Strings(md.Unused) 2402 | if !reflect.DeepEqual(md.Unused, expectedUnused) { 2403 | t.Fatalf("bad unused: %#v", md.Unused) 2404 | } 2405 | 2406 | expectedUnset := []string{ 2407 | "Vbar.Vbool", "Vbar.Vdata", "Vbar.Vextra", "Vbar.Vfloat", "Vbar.Vint", 2408 | "Vbar.VjsonFloat", "Vbar.VjsonInt", "Vbar.VjsonNumber"} 2409 | sort.Strings(md.Unset) 2410 | if !reflect.DeepEqual(md.Unset, expectedUnset) { 2411 | t.Fatalf("bad unset: %#v", md.Unset) 2412 | } 2413 | } 2414 | 2415 | func TestMetadata_Embedded(t *testing.T) { 2416 | t.Parallel() 2417 | 2418 | input := map[string]interface{}{ 2419 | "vstring": "foo", 2420 | "vunique": "bar", 2421 | } 2422 | 2423 | var md Metadata 2424 | var result EmbeddedSquash 2425 | config := &DecoderConfig{ 2426 | Metadata: &md, 2427 | Result: &result, 2428 | } 2429 | 2430 | decoder, err := NewDecoder(config) 2431 | if err != nil { 2432 | t.Fatalf("err: %s", err) 2433 | } 2434 | 2435 | err = decoder.Decode(input) 2436 | if err != nil { 2437 | t.Fatalf("err: %s", err.Error()) 2438 | } 2439 | 2440 | expectedKeys := []string{"Vstring", "Vunique"} 2441 | 2442 | sort.Strings(md.Keys) 2443 | if !reflect.DeepEqual(md.Keys, expectedKeys) { 2444 | t.Fatalf("bad keys: %#v", md.Keys) 2445 | } 2446 | 2447 | expectedUnused := []string{} 2448 | if !reflect.DeepEqual(md.Unused, expectedUnused) { 2449 | t.Fatalf("bad unused: %#v", md.Unused) 2450 | } 2451 | } 2452 | 2453 | func TestNonPtrValue(t *testing.T) { 2454 | t.Parallel() 2455 | 2456 | err := Decode(map[string]interface{}{}, Basic{}) 2457 | if err == nil { 2458 | t.Fatal("error should exist") 2459 | } 2460 | 2461 | if err.Error() != "result must be a pointer" { 2462 | t.Errorf("got unexpected error: %s", err) 2463 | } 2464 | } 2465 | 2466 | func TestTagged(t *testing.T) { 2467 | t.Parallel() 2468 | 2469 | input := map[string]interface{}{ 2470 | "foo": "bar", 2471 | "bar": "value", 2472 | } 2473 | 2474 | var result Tagged 2475 | err := Decode(input, &result) 2476 | if err != nil { 2477 | t.Fatalf("unexpected error: %s", err) 2478 | } 2479 | 2480 | if result.Value != "bar" { 2481 | t.Errorf("value should be 'bar', got: %#v", result.Value) 2482 | } 2483 | 2484 | if result.Extra != "value" { 2485 | t.Errorf("extra should be 'value', got: %#v", result.Extra) 2486 | } 2487 | } 2488 | 2489 | func TestWeakDecode(t *testing.T) { 2490 | t.Parallel() 2491 | 2492 | input := map[string]interface{}{ 2493 | "foo": "4", 2494 | "bar": "value", 2495 | } 2496 | 2497 | var result struct { 2498 | Foo int 2499 | Bar string 2500 | } 2501 | 2502 | if err := WeakDecode(input, &result); err != nil { 2503 | t.Fatalf("err: %s", err) 2504 | } 2505 | if result.Foo != 4 { 2506 | t.Fatalf("bad: %#v", result) 2507 | } 2508 | if result.Bar != "value" { 2509 | t.Fatalf("bad: %#v", result) 2510 | } 2511 | } 2512 | 2513 | func TestWeakDecodeMetadata(t *testing.T) { 2514 | t.Parallel() 2515 | 2516 | input := map[string]interface{}{ 2517 | "foo": "4", 2518 | "bar": "value", 2519 | "unused": "value", 2520 | "unexported": "value", 2521 | } 2522 | 2523 | var md Metadata 2524 | var result struct { 2525 | Foo int 2526 | Bar string 2527 | unexported string 2528 | } 2529 | 2530 | if err := WeakDecodeMetadata(input, &result, &md); err != nil { 2531 | t.Fatalf("err: %s", err) 2532 | } 2533 | if result.Foo != 4 { 2534 | t.Fatalf("bad: %#v", result) 2535 | } 2536 | if result.Bar != "value" { 2537 | t.Fatalf("bad: %#v", result) 2538 | } 2539 | 2540 | expectedKeys := []string{"Bar", "Foo"} 2541 | sort.Strings(md.Keys) 2542 | if !reflect.DeepEqual(md.Keys, expectedKeys) { 2543 | t.Fatalf("bad keys: %#v", md.Keys) 2544 | } 2545 | 2546 | expectedUnused := []string{"unexported", "unused"} 2547 | sort.Strings(md.Unused) 2548 | if !reflect.DeepEqual(md.Unused, expectedUnused) { 2549 | t.Fatalf("bad unused: %#v", md.Unused) 2550 | } 2551 | } 2552 | 2553 | func TestDecode_StructTaggedWithOmitempty_OmitEmptyValues(t *testing.T) { 2554 | t.Parallel() 2555 | 2556 | input := &StructWithOmitEmpty{} 2557 | 2558 | var emptySlice []interface{} 2559 | var emptyMap map[string]interface{} 2560 | var emptyNested *Nested 2561 | expected := &map[string]interface{}{ 2562 | "visible-string": "", 2563 | "visible-int": 0, 2564 | "visible-float": 0.0, 2565 | "visible-slice": emptySlice, 2566 | "visible-map": emptyMap, 2567 | "visible-nested": emptyNested, 2568 | } 2569 | 2570 | actual := &map[string]interface{}{} 2571 | Decode(input, actual) 2572 | 2573 | if !reflect.DeepEqual(actual, expected) { 2574 | t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual) 2575 | } 2576 | } 2577 | 2578 | func TestDecode_StructTaggedWithOmitempty_KeepNonEmptyValues(t *testing.T) { 2579 | t.Parallel() 2580 | 2581 | input := &StructWithOmitEmpty{ 2582 | VisibleStringField: "", 2583 | OmitStringField: "string", 2584 | VisibleIntField: 0, 2585 | OmitIntField: 1, 2586 | VisibleFloatField: 0.0, 2587 | OmitFloatField: 1.0, 2588 | VisibleSliceField: nil, 2589 | OmitSliceField: []interface{}{1}, 2590 | VisibleMapField: nil, 2591 | OmitMapField: map[string]interface{}{"k": "v"}, 2592 | NestedField: nil, 2593 | OmitNestedField: &Nested{}, 2594 | } 2595 | 2596 | var emptySlice []interface{} 2597 | var emptyMap map[string]interface{} 2598 | var emptyNested *Nested 2599 | expected := &map[string]interface{}{ 2600 | "visible-string": "", 2601 | "omittable-string": "string", 2602 | "visible-int": 0, 2603 | "omittable-int": 1, 2604 | "visible-float": 0.0, 2605 | "omittable-float": 1.0, 2606 | "visible-slice": emptySlice, 2607 | "omittable-slice": []interface{}{1}, 2608 | "visible-map": emptyMap, 2609 | "omittable-map": map[string]interface{}{"k": "v"}, 2610 | "visible-nested": emptyNested, 2611 | "omittable-nested": &Nested{}, 2612 | } 2613 | 2614 | actual := &map[string]interface{}{} 2615 | Decode(input, actual) 2616 | 2617 | if !reflect.DeepEqual(actual, expected) { 2618 | t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual) 2619 | } 2620 | } 2621 | 2622 | func TestDecode_mapToStruct(t *testing.T) { 2623 | type Target struct { 2624 | String string 2625 | StringPtr *string 2626 | } 2627 | 2628 | expected := Target{ 2629 | String: "hello", 2630 | } 2631 | 2632 | var target Target 2633 | err := Decode(map[string]interface{}{ 2634 | "string": "hello", 2635 | "StringPtr": "goodbye", 2636 | }, &target) 2637 | if err != nil { 2638 | t.Fatalf("got error: %s", err) 2639 | } 2640 | 2641 | // Pointers fail reflect test so do those manually 2642 | if target.StringPtr == nil || *target.StringPtr != "goodbye" { 2643 | t.Fatalf("bad: %#v", target) 2644 | } 2645 | target.StringPtr = nil 2646 | 2647 | if !reflect.DeepEqual(target, expected) { 2648 | t.Fatalf("bad: %#v", target) 2649 | } 2650 | } 2651 | 2652 | func TestDecoder_MatchName(t *testing.T) { 2653 | t.Parallel() 2654 | 2655 | type Target struct { 2656 | FirstMatch string `mapstructure:"first_match"` 2657 | SecondMatch string 2658 | NoMatch string `mapstructure:"no_match"` 2659 | } 2660 | 2661 | input := map[string]interface{}{ 2662 | "first_match": "foo", 2663 | "SecondMatch": "bar", 2664 | "NO_MATCH": "baz", 2665 | } 2666 | 2667 | expected := Target{ 2668 | FirstMatch: "foo", 2669 | SecondMatch: "bar", 2670 | } 2671 | 2672 | var actual Target 2673 | config := &DecoderConfig{ 2674 | Result: &actual, 2675 | MatchName: func(mapKey, fieldName string) bool { 2676 | return mapKey == fieldName 2677 | }, 2678 | } 2679 | 2680 | decoder, err := NewDecoder(config) 2681 | if err != nil { 2682 | t.Fatalf("err: %s", err) 2683 | } 2684 | 2685 | err = decoder.Decode(input) 2686 | if err != nil { 2687 | t.Fatalf("err: %s", err) 2688 | } 2689 | 2690 | if !reflect.DeepEqual(expected, actual) { 2691 | t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual) 2692 | } 2693 | } 2694 | 2695 | func TestDecoder_IgnoreUntaggedFields(t *testing.T) { 2696 | type Input struct { 2697 | UntaggedNumber int 2698 | TaggedNumber int `mapstructure:"tagged_number"` 2699 | UntaggedString string 2700 | TaggedString string `mapstructure:"tagged_string"` 2701 | } 2702 | input := &Input{ 2703 | UntaggedNumber: 31, 2704 | TaggedNumber: 42, 2705 | UntaggedString: "hidden", 2706 | TaggedString: "visible", 2707 | } 2708 | 2709 | actual := make(map[string]interface{}) 2710 | config := &DecoderConfig{ 2711 | Result: &actual, 2712 | IgnoreUntaggedFields: true, 2713 | } 2714 | 2715 | decoder, err := NewDecoder(config) 2716 | if err != nil { 2717 | t.Fatalf("err: %s", err) 2718 | } 2719 | 2720 | err = decoder.Decode(input) 2721 | if err != nil { 2722 | t.Fatalf("err: %s", err) 2723 | } 2724 | 2725 | expected := map[string]interface{}{ 2726 | "tagged_number": 42, 2727 | "tagged_string": "visible", 2728 | } 2729 | 2730 | if !reflect.DeepEqual(expected, actual) { 2731 | t.Fatalf("Decode() expected: %#v\ngot: %#v", expected, actual) 2732 | } 2733 | } 2734 | 2735 | func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) { 2736 | var result Slice 2737 | err := Decode(input, &result) 2738 | if err != nil { 2739 | t.Fatalf("got error: %s", err) 2740 | } 2741 | 2742 | if result.Vfoo != expected.Vfoo { 2743 | t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo) 2744 | } 2745 | 2746 | if result.Vbar == nil { 2747 | t.Fatalf("Vbar a slice, got '%#v'", result.Vbar) 2748 | } 2749 | 2750 | if len(result.Vbar) != len(expected.Vbar) { 2751 | t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar)) 2752 | } 2753 | 2754 | for i, v := range result.Vbar { 2755 | if v != expected.Vbar[i] { 2756 | t.Errorf( 2757 | "Vbar[%d] should be '%#v', got '%#v'", 2758 | i, expected.Vbar[i], v) 2759 | } 2760 | } 2761 | } 2762 | 2763 | func testArrayInput(t *testing.T, input map[string]interface{}, expected *Array) { 2764 | var result Array 2765 | err := Decode(input, &result) 2766 | if err != nil { 2767 | t.Fatalf("got error: %s", err) 2768 | } 2769 | 2770 | if result.Vfoo != expected.Vfoo { 2771 | t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo) 2772 | } 2773 | 2774 | if result.Vbar == [2]string{} { 2775 | t.Fatalf("Vbar a slice, got '%#v'", result.Vbar) 2776 | } 2777 | 2778 | if len(result.Vbar) != len(expected.Vbar) { 2779 | t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar)) 2780 | } 2781 | 2782 | for i, v := range result.Vbar { 2783 | if v != expected.Vbar[i] { 2784 | t.Errorf( 2785 | "Vbar[%d] should be '%#v', got '%#v'", 2786 | i, expected.Vbar[i], v) 2787 | } 2788 | } 2789 | } 2790 | 2791 | func stringPtr(v string) *string { return &v } 2792 | func intPtr(v int) *int { return &v } 2793 | func uintPtr(v uint) *uint { return &v } 2794 | func boolPtr(v bool) *bool { return &v } 2795 | func floatPtr(v float64) *float64 { return &v } 2796 | func interfacePtr(v interface{}) *interface{} { return &v } 2797 | --------------------------------------------------------------------------------