122122// field value is zero and a numeric type, the field is empty, and it won't
123123// be encoded into the destination type.
124124//
125- // type Source {
125+ // type Source struct {
126126// Age int `mapstructure:",omitempty"`
127127// }
128128//
@@ -192,7 +192,7 @@ type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface
192192// source and target types.
193193type DecodeHookFuncKind func (reflect.Kind , reflect.Kind , interface {}) (interface {}, error )
194194
195- // DecodeHookFuncRaw is a DecodeHookFunc which has complete access to both the source and target
195+ // DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target
196196// values.
197197type DecodeHookFuncValue func (from reflect.Value , to reflect.Value ) (interface {}, error )
198198
@@ -215,6 +215,12 @@ type DecoderConfig struct {
215215 // (extra keys).
216216 ErrorUnused bool
217217
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+
218224 // ZeroFields, if set to true, will zero fields before writing them.
219225 // For example, a map will be emptied before decoded values are put in
220226 // it. If this is false, a map will be merged.
@@ -258,6 +264,15 @@ type DecoderConfig struct {
258264 // The tag name that mapstructure reads for field names. This
259265 // defaults to "mapstructure"
260266 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
261276}
262277
263278// A Decoder takes a raw interface value and turns it into structured
@@ -279,6 +294,11 @@ type Metadata struct {
279294 // Unused is a slice of keys that were found in the raw value but
280295 // weren't decoded since there was no matching field in the result interface
281296 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
282302}
283303
284304// Decode takes an input structure and uses reflection to translate it to
@@ -370,12 +390,20 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
370390 if config .Metadata .Unused == nil {
371391 config .Metadata .Unused = make ([]string , 0 )
372392 }
393+
394+ if config .Metadata .Unset == nil {
395+ config .Metadata .Unset = make ([]string , 0 )
396+ }
373397 }
374398
375399 if config .TagName == "" {
376400 config .TagName = "mapstructure"
377401 }
378402
403+ if config .MatchName == nil {
404+ config .MatchName = strings .EqualFold
405+ }
406+
379407 result := & Decoder {
380408 config : config ,
381409 }
@@ -675,16 +703,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
675703 }
676704 case dataType .PkgPath () == "encoding/json" && dataType .Name () == "Number" :
677705 jn := data .(json.Number )
678- i , err := jn . Int64 ( )
706+ i , err := strconv . ParseUint ( string ( jn ), 0 , 64 )
679707 if err != nil {
680708 return fmt .Errorf (
681709 "error decoding json.Number into %s: %s" , name , err )
682710 }
683- if i < 0 && ! d .config .WeaklyTypedInput {
684- return fmt .Errorf ("cannot parse '%s', %d overflows uint" ,
685- name , i )
686- }
687- val .SetUint (uint64 (i ))
711+ val .SetUint (i )
688712 default :
689713 return fmt .Errorf (
690714 "'%s' expected type '%s', got unconvertible type '%s', value: '%v'" ,
@@ -901,9 +925,15 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
901925 tagValue := f .Tag .Get (d .config .TagName )
902926 keyName := f .Name
903927
928+ if tagValue == "" && d .config .IgnoreUntaggedFields {
929+ continue
930+ }
931+
904932 // If Squash is set in the config, we squash the field down.
905933 squash := d .config .Squash && v .Kind () == reflect .Struct && f .Anonymous
906934
935+ v = dereferencePtrToStructIfNeeded (v , d .config .TagName )
936+
907937 // Determine the name of the key in the map
908938 if index := strings .Index (tagValue , "," ); index != - 1 {
909939 if tagValue [:index ] == "-" {
@@ -915,7 +945,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
915945 }
916946
917947 // If "squash" is specified in the tag, we squash the field down.
918- squash = ! squash && strings .Index (tagValue [index + 1 :], "squash" ) != - 1
948+ squash = squash || strings .Index (tagValue [index + 1 :], "squash" ) != - 1
919949 if squash {
920950 // When squashing, the embedded type can be a pointer to a struct.
921951 if v .Kind () == reflect .Ptr && v .Elem ().Kind () == reflect .Struct {
@@ -1083,7 +1113,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
10831113 }
10841114
10851115 // If the input value is nil, then don't allocate since empty != nil
1086- if dataVal .IsNil () {
1116+ if dataValKind != reflect . Array && dataVal .IsNil () {
10871117 return nil
10881118 }
10891119
@@ -1245,6 +1275,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
12451275 dataValKeysUnused [dataValKey .Interface ()] = struct {}{}
12461276 }
12471277
1278+ targetValKeysUnused := make (map [interface {}]struct {})
12481279 errors := make ([]string , 0 )
12491280
12501281 // This slice will keep track of all the structs we'll be decoding.
@@ -1340,7 +1371,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
13401371 continue
13411372 }
13421373
1343- if strings . EqualFold (mK , fieldName ) {
1374+ if d . config . MatchName (mK , fieldName ) {
13441375 rawMapKey = dataValKey
13451376 rawMapVal = dataVal .MapIndex (dataValKey )
13461377 break
@@ -1349,7 +1380,8 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
13491380
13501381 if ! rawMapVal .IsValid () {
13511382 // There was no matching key in the map for the value in
1352- // the struct. Just ignore.
1383+ // the struct. Remember it for potential errors and metadata.
1384+ targetValKeysUnused [fieldName ] = struct {}{}
13531385 continue
13541386 }
13551387 }
@@ -1409,6 +1441,17 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
14091441 errors = appendErrors (errors , err )
14101442 }
14111443
1444+ if d .config .ErrorUnset && len (targetValKeysUnused ) > 0 {
1445+ keys := make ([]string , 0 , len (targetValKeysUnused ))
1446+ for rawKey := range targetValKeysUnused {
1447+ keys = append (keys , rawKey .(string ))
1448+ }
1449+ sort .Strings (keys )
1450+
1451+ err := fmt .Errorf ("'%s' has unset fields: %s" , name , strings .Join (keys , ", " ))
1452+ errors = appendErrors (errors , err )
1453+ }
1454+
14121455 if len (errors ) > 0 {
14131456 return & Error {errors }
14141457 }
@@ -1423,6 +1466,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
14231466
14241467 d .config .Metadata .Unused = append (d .config .Metadata .Unused , key )
14251468 }
1469+ for rawKey := range targetValKeysUnused {
1470+ key := rawKey .(string )
1471+ if name != "" {
1472+ key = name + "." + key
1473+ }
1474+
1475+ d .config .Metadata .Unset = append (d .config .Metadata .Unset , key )
1476+ }
14261477 }
14271478
14281479 return nil
@@ -1460,3 +1511,28 @@ func getKind(val reflect.Value) reflect.Kind {
14601511 return kind
14611512 }
14621513}
1514+
1515+ func isStructTypeConvertibleToMap (typ reflect.Type , checkMapstructureTags bool , tagName string ) bool {
1516+ for i := 0 ; i < typ .NumField (); i ++ {
1517+ f := typ .Field (i )
1518+ if f .PkgPath == "" && ! checkMapstructureTags { // check for unexported fields
1519+ return true
1520+ }
1521+ if checkMapstructureTags && f .Tag .Get (tagName ) != "" { // check for mapstructure tags inside
1522+ return true
1523+ }
1524+ }
1525+ return false
1526+ }
1527+
1528+ func dereferencePtrToStructIfNeeded (v reflect.Value , tagName string ) reflect.Value {
1529+ if v .Kind () != reflect .Ptr || v .Elem ().Kind () != reflect .Struct {
1530+ return v
1531+ }
1532+ deref := v .Elem ()
1533+ derefT := deref .Type ()
1534+ if isStructTypeConvertibleToMap (derefT , true , tagName ) {
1535+ return deref
1536+ }
1537+ return v
1538+ }
0 commit comments