Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect nil slices, maps in bson encoder #147

Merged
merged 14 commits into from
May 9, 2018
56 changes: 56 additions & 0 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
15 changes: 14 additions & 1 deletion bson/compatibility.go
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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
}
6 changes: 6 additions & 0 deletions bson/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/sasl/sasl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/scram/scram.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down