@@ -215,6 +215,11 @@ 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).
221+ ErrorUnset bool
222+
218223 // ZeroFields, if set to true, will zero fields before writing them.
219224 // For example, a map will be emptied before decoded values are put in
220225 // it. If this is false, a map will be merged.
@@ -288,6 +293,11 @@ type Metadata struct {
288293 // Unused is a slice of keys that were found in the raw value but
289294 // weren't decoded since there was no matching field in the result interface
290295 Unused []string
296+
297+ // Unset is a slice of field names that were found in the result interface
298+ // but weren't set in the decoding process since there was no matching value
299+ // in the input
300+ Unset []string
291301}
292302
293303// Decode takes an input structure and uses reflection to translate it to
@@ -379,6 +389,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
379389 if config .Metadata .Unused == nil {
380390 config .Metadata .Unused = make ([]string , 0 )
381391 }
392+
393+ if config .Metadata .Unset == nil {
394+ config .Metadata .Unset = make ([]string , 0 )
395+ }
382396 }
383397
384398 if config .TagName == "" {
@@ -1260,6 +1274,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
12601274 dataValKeysUnused [dataValKey .Interface ()] = struct {}{}
12611275 }
12621276
1277+ targetValKeysUnused := make (map [interface {}]struct {})
12631278 errors := make ([]string , 0 )
12641279
12651280 // This slice will keep track of all the structs we'll be decoding.
@@ -1364,7 +1379,8 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
13641379
13651380 if ! rawMapVal .IsValid () {
13661381 // There was no matching key in the map for the value in
1367- // the struct. Just ignore.
1382+ // the struct. Remember it for potential errors and metadata.
1383+ targetValKeysUnused [fieldName ] = struct {}{}
13681384 continue
13691385 }
13701386 }
@@ -1424,6 +1440,17 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
14241440 errors = appendErrors (errors , err )
14251441 }
14261442
1443+ if d .config .ErrorUnset && len (targetValKeysUnused ) > 0 {
1444+ keys := make ([]string , 0 , len (targetValKeysUnused ))
1445+ for rawKey := range targetValKeysUnused {
1446+ keys = append (keys , rawKey .(string ))
1447+ }
1448+ sort .Strings (keys )
1449+
1450+ err := fmt .Errorf ("'%s' has unset fields: %s" , name , strings .Join (keys , ", " ))
1451+ errors = appendErrors (errors , err )
1452+ }
1453+
14271454 if len (errors ) > 0 {
14281455 return & Error {errors }
14291456 }
@@ -1438,6 +1465,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
14381465
14391466 d .config .Metadata .Unused = append (d .config .Metadata .Unused , key )
14401467 }
1468+ for rawKey := range targetValKeysUnused {
1469+ key := rawKey .(string )
1470+ if name != "" {
1471+ key = name + "." + key
1472+ }
1473+
1474+ d .config .Metadata .Unset = append (d .config .Metadata .Unset , key )
1475+ }
14411476 }
14421477
14431478 return nil
0 commit comments