diff --git a/dynamodb/marshaller.go b/dynamodb/marshaller.go index 898b0d48..2898fbda 100644 --- a/dynamodb/marshaller.go +++ b/dynamodb/marshaller.go @@ -20,7 +20,7 @@ func MarshalAttributes(m interface{}) ([]Attribute, error) { builder.buffer = []Attribute{} for _, f := range cachedTypeFields(v.Type()) { // loop on each field fv := fieldByIndex(v, f.index) - if !fv.IsValid() || isEmptyValue(fv) { + if !fv.IsValid() || isEmptyValueToOmit(fv) { continue } @@ -293,6 +293,18 @@ func numericReflectedValueString(v reflect.Value) (string, error) { return "", fmt.Errorf("UnsupportedNumericValueError %#v", v.Type()) } +// In DynamoDB we should omit empty value in some type +// See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html +func isEmptyValueToOmit(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface, reflect.Ptr: + // should omit if empty value + return isEmptyValue(v) + } + // otherwise should not omit + return false +} + // ---------------- Below are copied handy functions from http://golang.org/src/pkg/encoding/json/encode.go -------------------------------- func isEmptyValue(v reflect.Value) bool { switch v.Kind() { diff --git a/dynamodb/marshaller_test.go b/dynamodb/marshaller_test.go index 3a9d3ab6..f6a90a21 100644 --- a/dynamodb/marshaller_test.go +++ b/dynamodb/marshaller_test.go @@ -65,6 +65,10 @@ func testObjectTime() *TestStructTime { } } +func testObjectWithZeroValues() *TestStruct { + return &TestStruct{} +} + func testObjectWithNilSets() *TestStruct { return &TestStruct{ TestBool: true, @@ -135,6 +139,19 @@ func testAttrsTime() []dynamodb.Attribute { } } +func testAttrsWithZeroValues() []dynamodb.Attribute { + return []dynamodb.Attribute{ + dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":false,"SubInt":0,"SubString":"","SubStringArray":null}`, SetValues: []string(nil)}, + } +} + func testAttrsWithNilSets() []dynamodb.Attribute { return []dynamodb.Attribute{ dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "1", SetValues: []string(nil)}, @@ -224,6 +241,17 @@ func (s *MarshallerSuite) TestMarshalNilSets(c *gocheck.C) { c.Check(attrs, gocheck.DeepEquals, expected) } +func (s *MarshallerSuite) TestMarshalZeroValues(c *gocheck.C) { + testObj := testObjectWithZeroValues() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrsWithZeroValues() + c.Check(attrs, gocheck.DeepEquals, expected) +} + func (s *MarshallerSuite) TestMarshalEmptySets(c *gocheck.C) { testObj := testObjectWithEmptySets() attrs, err := dynamodb.MarshalAttributes(testObj)