diff --git a/decode.go b/decode.go index 660de03..29a4c30 100644 --- a/decode.go +++ b/decode.go @@ -401,14 +401,16 @@ func unmarshalItem(item map[string]*dynamodb.AttributeValue, out interface{}) er var err error fields := fieldsInStruct(rv.Elem()) for name, fv := range fields { + // we need to zero-out all fields to avoid weird data sticking around + // when iterating by unmarshaling to the same object over and over + if fv.CanSet() { + fv.Set(reflect.Zero(fv.Type())) + } + if av, ok := item[name]; ok { if innerErr := unmarshalReflect(av, fv); innerErr != nil { err = innerErr } - } else { - // we need to zero-out omitted fields to avoid weird data sticking around - // when iterating by unmarshaling to the same object over and over - fv.Set(reflect.Zero(fv.Type())) } } return err diff --git a/decode_test.go b/decode_test.go index 0686dfc..dcecdf5 100644 --- a/decode_test.go +++ b/decode_test.go @@ -249,3 +249,33 @@ func TestUnmarshalMissing(t *testing.T) { t.Error("bad unmarshal missing. want:", want, "got:", w) } } + +func TestUnmarshalClearFields(t *testing.T) { + // tests against a regression from v1.12.0 in which map fields were not properly getting reset + + type Foo struct { + Map map[string]bool + } + + items := []Foo{ + {Map: map[string]bool{"a": true}}, + {Map: map[string]bool{"b": true}}, // before fix: {a: true, b: true} + {Map: map[string]bool{"c": true}}, // before fix: {a: true, b: true, c: true} + } + + var foo Foo + for _, item := range items { + raw, err := MarshalItem(item) + if err != nil { + t.Fatal(err) + } + + if err := UnmarshalItem(raw, &foo); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(item, foo) { + t.Error("bad result. want:", item, "got:", foo) + } + } +}