Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bson/bsoncodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type EncodeContext struct {
nilSliceAsEmpty bool
nilByteSliceAsEmpty bool
omitZeroStruct bool
omitEmpty bool
useJSONStructTags bool
}

Expand Down
9 changes: 8 additions & 1 deletion bson/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,21 @@ func (e *Encoder) NilByteSliceAsEmpty() {
// TODO struct fields once the logic is updated to also inspect private struct fields.

// OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{})
// as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set.
// as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set
// or the OmitEmpty() method is called.
//
// Note that the Encoder only examines exported struct fields when determining if a struct is the
// zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty.
func (e *Encoder) OmitZeroStruct() {
e.ec.omitZeroStruct = true
}

// OmitEmpty causes the Encoder to omit empty values from the marshaled BSON as the "omitempty"
// struct tag option is set.
func (e *Encoder) OmitEmpty() {
e.ec.omitEmpty = true
}

// UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson"
// struct tag is not specified.
func (e *Encoder) UseJSONStructTags() {
Expand Down
13 changes: 13 additions & 0 deletions bson/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,19 @@ func TestEncoderConfiguration(t *testing.T) {
}{},
want: bsoncore.NewDocumentBuilder().Build(),
},
// Test that OmitZeroStruct omits empty structs from the marshaled document if
// OmitEmpty is also set.
{
description: "OmitEmpty",
configure: func(enc *Encoder) {
enc.OmitZeroStruct()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest renaming this test "OmitEmpty with non-zeroer struct".

Additionally, we need to creating a new test where enc.OmitZeroStruct() is not used in the configuration. The new test should include the types referenced in the documentation and should be called "OmitEmpty":

Numbers, booleans, and strings are considered empty if their value is equal to the zero value for the type (i.e. 0 for numbers, false for booleans, and "" for strings). Slices, maps, and arrays are considered empty if they are of length zero. Interfaces and pointers are considered empty if their value is nil. By default, structs are only considered empty if the struct type implements bsoncodec.Zeroer and the IsZero method returns true. Struct types that do not implement bsoncodec.Zeroer are never considered empty and will be marshaled as embedded documents.

type zeroer struct {
	MyString string
}

func (zeroer) IsZero() bool {
	return true
}

var _ bson.Zeroer = zeroer{}

type myStruct struct {
	Zero    zeroer
	I64     int64
	F64     float64
	String  string
	Boolean bool
	Array   []int
	Map     map[string]int
	Bytes   []byte
	Time    time.Time
	Pointer *int
}

enc.OmitEmpty()
},
input: struct {
Zero zeroStruct
}{},
want: bsoncore.NewDocumentBuilder().Build(),
},
// Test that UseJSONStructTags causes the Encoder to fall back to "json" struct tags if
// "bson" struct tags are not available.
{
Expand Down
4 changes: 4 additions & 0 deletions bson/struct_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ func (sc *structCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect
}
}

if ec.omitEmpty {
desc.omitEmpty = true
}

desc.encoder, rv, err = lookupElementEncoder(ec, desc.encoder, rv)

if err != nil && !errors.Is(err, errInvalidValue) {
Expand Down
13 changes: 13 additions & 0 deletions internal/integration/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,19 @@ func TestClient_BSONOptions(t *testing.T) {
want: &bson.D{},
wantRaw: bson.Raw(bsoncore.NewDocumentBuilder().Build()),
},
{
name: "OmitEmpty",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest renaming this test "OmitEmpty with non-zeroer struct".

bsonOpts: &options.BSONOptions{
OmitZeroStruct: true,
OmitEmpty: true,
},
doc: struct {
X jsonTagsTest `bson:"x"`
}{},
decodeInto: func() interface{} { return &bson.D{} },
want: &bson.D{},
wantRaw: bson.Raw(bsoncore.NewDocumentBuilder().Build()),
},
{
name: "StringifyMapKeysWithFmt",
bsonOpts: &options.BSONOptions{
Expand Down
3 changes: 3 additions & 0 deletions mongo/mongo.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ func getEncoder(
if opts.OmitZeroStruct {
enc.OmitZeroStruct()
}
if opts.OmitEmpty {
enc.OmitEmpty()
}
if opts.StringifyMapKeysWithFmt {
enc.StringifyMapKeysWithFmt()
}
Expand Down
5 changes: 4 additions & 1 deletion mongo/options/clientoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,12 @@ type BSONOptions struct {

// OmitZeroStruct causes the driver to consider the zero value for a struct
// (e.g. MyStruct{}) as empty and omit it from the marshaled BSON when the
// "omitempty" struct tag option is set.
// "omitempty" struct tag option or the "OmitEmpty" field is set.
OmitZeroStruct bool

// OmitEmpty causes the driver to omit empty values from the marshaled BSON.
OmitEmpty bool

// StringifyMapKeysWithFmt causes the driver to convert Go map keys to BSON
// document field name strings using fmt.Sprint instead of the default
// string conversion logic.
Expand Down
Loading