diff --git a/bson/bson_test.go b/bson/bson_test.go index 905f48ba1..5a389a498 100644 --- a/bson/bson_test.go +++ b/bson/bson_test.go @@ -1933,3 +1933,59 @@ func (s *S) BenchmarkNewObjectId(c *C) { bson.NewObjectId() } } + +func (s *S) TestMarshalRespectNil(c *C) { + type T struct { + Slice []int + SlicePtr *[]int + Ptr *int + Map map[string]interface{} + MapPtr *map[string]interface{} + } + + bson.SetRespectNilValues(true) + defer bson.SetRespectNilValues(false) + + testStruct1 := T{} + + c.Assert(testStruct1.Slice, IsNil) + c.Assert(testStruct1.SlicePtr, IsNil) + c.Assert(testStruct1.Map, IsNil) + c.Assert(testStruct1.MapPtr, IsNil) + c.Assert(testStruct1.Ptr, IsNil) + + b, _ := bson.Marshal(testStruct1) + + testStruct2 := T{} + + bson.Unmarshal(b, &testStruct2) + + c.Assert(testStruct2.Slice, IsNil) + c.Assert(testStruct2.SlicePtr, IsNil) + c.Assert(testStruct2.Map, IsNil) + c.Assert(testStruct2.MapPtr, IsNil) + c.Assert(testStruct2.Ptr, IsNil) + + testStruct1 = T{ + Slice: []int{}, + SlicePtr: &[]int{}, + Map: map[string]interface{}{}, + MapPtr: &map[string]interface{}{}, + } + + c.Assert(testStruct1.Slice, NotNil) + c.Assert(testStruct1.SlicePtr, NotNil) + c.Assert(testStruct1.Map, NotNil) + c.Assert(testStruct1.MapPtr, NotNil) + + b, _ = bson.Marshal(testStruct1) + + testStruct2 = T{} + + bson.Unmarshal(b, &testStruct2) + + c.Assert(testStruct2.Slice, NotNil) + c.Assert(testStruct2.SlicePtr, NotNil) + c.Assert(testStruct2.Map, NotNil) + c.Assert(testStruct2.MapPtr, NotNil) +} diff --git a/bson/compatibility.go b/bson/compatibility.go index 6afecf53c..66efd465f 100644 --- a/bson/compatibility.go +++ b/bson/compatibility.go @@ -1,7 +1,8 @@ package bson -// Current state of the JSON tag fallback option. +// Current state of the JSON tag fallback option. var useJSONTagFallback = false +var useRespectNilValues = false // SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures // without BSON tags on a field will fall-back to using the JSON tag (if present). @@ -14,3 +15,15 @@ func SetJSONTagFallback(state bool) { func JSONTagFallbackState() bool { return useJSONTagFallback } + +// SetRespectNilValues enables or disables serializing nil slices or maps to `null` values. +// In other words it enables `encoding/json` compatible behaviour. +func SetRespectNilValues(state bool) { + useRespectNilValues = state +} + +// RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option. +// See SetRespectNilValues for more information. +func RespectNilValuesState() bool { + return useRespectNilValues +} diff --git a/bson/encode.go b/bson/encode.go index 5f036edae..d0c6b2a85 100644 --- a/bson/encode.go +++ b/bson/encode.go @@ -245,6 +245,12 @@ func (e *encoder) addStruct(v reflect.Value) { if info.OmitEmpty && isZero(value) { continue } + if useRespectNilValues && + (value.Kind() == reflect.Slice || value.Kind() == reflect.Map) && + value.IsNil() { + e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize) + continue + } e.addElem(info.Key, value, info.MinSize) } } diff --git a/internal/sasl/sasl.go b/internal/sasl/sasl.go index 25a537426..0b56f0b6f 100644 --- a/internal/sasl/sasl.go +++ b/internal/sasl/sasl.go @@ -127,6 +127,7 @@ func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, er if rc == C.SASL_CONTINUE { return clientData, false, nil } + return nil, false, saslError(rc, ss.conn, "cannot establish SASL session") } diff --git a/internal/scram/scram.go b/internal/scram/scram.go index d3ddd02fd..03c14daf7 100644 --- a/internal/scram/scram.go +++ b/internal/scram/scram.go @@ -91,7 +91,7 @@ func NewClient(newHash func() hash.Hash, user, pass string) *Client { // Out returns the data to be sent to the server in the current step. func (c *Client) Out() []byte { if c.out.Len() == 0 { - return nil + return []byte{} } return c.out.Bytes() }