Skip to content

Commit

Permalink
GODRIVER-2796 Update ObjectIDAsHexString behavior.
Browse files Browse the repository at this point in the history
  • Loading branch information
qingyang-hu committed Jun 28, 2024
1 parent ea15f7d commit a5b9b2c
Show file tree
Hide file tree
Showing 15 changed files with 93 additions and 289 deletions.
145 changes: 9 additions & 136 deletions bson/bsoncodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,10 @@ func (vde ValueDecoderError) Error() string {
type EncodeContext struct {
*Registry

// MinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64,
// minSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64,
// uint, uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits)
// that can represent the integer value.
//
// Deprecated: Use bson.Encoder.IntMinSize instead.
MinSize bool
minSize bool

errorOnInlineDuplicates bool
stringifyMapKeysWithFmt bool
Expand All @@ -93,65 +91,6 @@ type EncodeContext struct {
useJSONStructTags bool
}

// ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in
// the marshaled BSON when the "inline" struct tag option is set.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.ErrorOnInlineDuplicates] instead.
func (ec *EncodeContext) ErrorOnInlineDuplicates() {
ec.errorOnInlineDuplicates = true
}

// StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name
// strings using fmt.Sprintf() instead of the default string conversion logic.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.StringifyMapKeysWithFmt] instead.
func (ec *EncodeContext) StringifyMapKeysWithFmt() {
ec.stringifyMapKeysWithFmt = true
}

// NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON
// null.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilMapAsEmpty] instead.
func (ec *EncodeContext) NilMapAsEmpty() {
ec.nilMapAsEmpty = true
}

// NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON
// null.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilSliceAsEmpty] instead.
func (ec *EncodeContext) NilSliceAsEmpty() {
ec.nilSliceAsEmpty = true
}

// NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values
// instead of BSON null.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilByteSliceAsEmpty] instead.
func (ec *EncodeContext) NilByteSliceAsEmpty() {
ec.nilByteSliceAsEmpty = true
}

// 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.
//
// 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.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.OmitZeroStruct] instead.
func (ec *EncodeContext) OmitZeroStruct() {
ec.omitZeroStruct = true
}

// UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson"
// struct tag is not specified.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.UseJSONStructTags] instead.
func (ec *EncodeContext) UseJSONStructTags() {
ec.useJSONStructTags = true
}

// DecodeContext is the contextual information required for a Codec to decode a
// value.
type DecodeContext struct {
Expand All @@ -161,9 +100,7 @@ type DecodeContext struct {
// values when attempting to unmarshal them into a Go integer (int, int8, int16, int32, int64,
// uint, uint8, uint16, uint32, or uint64) struct field. The truncation logic does not apply to
// BSON "decimal128" values.
//
// Deprecated: Use bson.Decoder.AllowTruncatingDoubles instead.
Truncate bool
truncate bool

// Ancestor is the type of a containing document. This is mainly used to determine what type
// should be used when decoding an embedded document into an empty interface. For example, if
Expand All @@ -179,76 +116,12 @@ type DecodeContext struct {
// error. DocumentType overrides the Ancestor field.
defaultDocumentType reflect.Type

binaryAsSlice bool
useJSONStructTags bool
useLocalTimeZone bool
zeroMaps bool
zeroStructs bool
}

// BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the "Generic" or
// "Old" BSON binary subtype as a Go byte slice instead of a Binary.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.BinaryAsSlice] instead.
func (dc *DecodeContext) BinaryAsSlice() {
dc.binaryAsSlice = true
}

// UseJSONStructTags causes the Decoder to fall back to using the "json" struct tag if a "bson"
// struct tag is not specified.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseJSONStructTags] instead.
func (dc *DecodeContext) UseJSONStructTags() {
dc.useJSONStructTags = true
}

// UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead
// of the UTC timezone.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseLocalTimeZone] instead.
func (dc *DecodeContext) UseLocalTimeZone() {
dc.useLocalTimeZone = true
}

// ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value
// passed to Decode before unmarshaling BSON documents into them.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroMaps] instead.
func (dc *DecodeContext) ZeroMaps() {
dc.zeroMaps = true
}

// ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination
// value passed to Decode before unmarshaling BSON documents into them.
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroStructs] instead.
func (dc *DecodeContext) ZeroStructs() {
dc.zeroStructs = true
}

// DefaultDocumentM causes the Decoder to always unmarshal documents into the M type. This
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentM] instead.
func (dc *DecodeContext) DefaultDocumentM() {
dc.defaultDocumentType = reflect.TypeOf(M{})
}

// DefaultDocumentD causes the Decoder to always unmarshal documents into the D type. This
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
//
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentD] instead.
func (dc *DecodeContext) DefaultDocumentD() {
dc.defaultDocumentType = reflect.TypeOf(D{})
}

// ValueCodec is an interface for encoding and decoding a reflect.Value.
// values.
//
// Deprecated: Use [ValueEncoder] and [ValueDecoder] instead.
type ValueCodec interface {
ValueEncoder
ValueDecoder
binaryAsSlice bool
objectIDAsHexString bool
useJSONStructTags bool
useLocalTimeZone bool
zeroMaps bool
zeroStructs bool
}

// ValueEncoder is the interface implemented by types that can encode a provided Go type to BSON.
Expand Down
52 changes: 13 additions & 39 deletions bson/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,6 @@ var decPool = sync.Pool{
type Decoder struct {
dc DecodeContext
vr ValueReader

defaultDocumentM bool
defaultDocumentD bool

binaryAsSlice bool
useJSONStructTags bool
useLocalTimeZone bool
zeroMaps bool
zeroStructs bool
}

// NewDecoder returns a new decoder that uses the DefaultRegistry to read from vr.
Expand Down Expand Up @@ -82,28 +73,6 @@ func (d *Decoder) Decode(val interface{}) error {
return err
}

if d.defaultDocumentM {
d.dc.DefaultDocumentM()
}
if d.defaultDocumentD {
d.dc.DefaultDocumentD()
}
if d.binaryAsSlice {
d.dc.BinaryAsSlice()
}
if d.useJSONStructTags {
d.dc.UseJSONStructTags()
}
if d.useLocalTimeZone {
d.dc.UseLocalTimeZone()
}
if d.zeroMaps {
d.dc.ZeroMaps()
}
if d.zeroStructs {
d.dc.ZeroStructs()
}

return decoder.DecodeValue(d.dc, d.vr, rval)
}

Expand All @@ -121,48 +90,53 @@ func (d *Decoder) SetRegistry(r *Registry) {
// DefaultDocumentM causes the Decoder to always unmarshal documents into the primitive.M type. This
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
func (d *Decoder) DefaultDocumentM() {
d.defaultDocumentM = true
d.dc.defaultDocumentType = reflect.TypeOf(M{})
}

// DefaultDocumentD causes the Decoder to always unmarshal documents into the primitive.D type. This
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
func (d *Decoder) DefaultDocumentD() {
d.defaultDocumentD = true
d.dc.defaultDocumentType = reflect.TypeOf(D{})
}

// AllowTruncatingDoubles causes the Decoder to truncate the fractional part of BSON "double" values
// when attempting to unmarshal them into a Go integer (int, int8, int16, int32, or int64) struct
// field. The truncation logic does not apply to BSON "decimal128" values.
func (d *Decoder) AllowTruncatingDoubles() {
d.dc.Truncate = true
d.dc.truncate = true
}

// BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the "Generic" or
// "Old" BSON binary subtype as a Go byte slice instead of a primitive.Binary.
func (d *Decoder) BinaryAsSlice() {
d.binaryAsSlice = true
d.dc.binaryAsSlice = true
}

// ObjectIDAsHexString causes the Decoder to decode object IDs to their hex representation.
func (d *Decoder) ObjectIDAsHexString() {
d.dc.objectIDAsHexString = true
}

// UseJSONStructTags causes the Decoder to fall back to using the "json" struct tag if a "bson"
// struct tag is not specified.
func (d *Decoder) UseJSONStructTags() {
d.useJSONStructTags = true
d.dc.useJSONStructTags = true
}

// UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead
// of the UTC timezone.
func (d *Decoder) UseLocalTimeZone() {
d.useLocalTimeZone = true
d.dc.useLocalTimeZone = true
}

// ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value
// passed to Decode before unmarshaling BSON documents into them.
func (d *Decoder) ZeroMaps() {
d.zeroMaps = true
d.dc.zeroMaps = true
}

// ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination
// value passed to Decode before unmarshaling BSON documents into them.
func (d *Decoder) ZeroStructs() {
d.zeroStructs = true
d.dc.zeroStructs = true
}
19 changes: 19 additions & 0 deletions bson/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ func TestDecoderConfiguration(t *testing.T) {
MyUint64 uint64
}

type objectIDTest struct {
ID string
}

type jsonStructTest struct {
StructFieldName string `json:"jsonFieldName"`
}
Expand Down Expand Up @@ -512,6 +516,21 @@ func TestDecoderConfiguration(t *testing.T) {
{Key: "myDocument", Value: M{"myString": "test value"}},
},
},
// Test that ObjectIDAsHexString causes the Decoder to decode object ID to hex.
{
description: "ObjectIDAsHexString",
configure: func(dec *Decoder) {
dec.ObjectIDAsHexString()
},
input: bsoncore.NewDocumentBuilder().
AppendObjectID("id", func() ObjectID {
id, _ := ObjectIDFromHex("5ef7fdd91c19e3222b41b839")
return id
}()).
Build(),
decodeInto: func() interface{} { return &objectIDTest{} },
want: &objectIDTest{ID: "5ef7fdd91c19e3222b41b839"},
},
// Test that UseJSONStructTags causes the Decoder to fall back to "json" struct tags if
// "bson" struct tags are not available.
{
Expand Down
4 changes: 2 additions & 2 deletions bson/default_value_decoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func intDecodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Va
if err != nil {
return emptyValue, err
}
if !dc.Truncate && math.Floor(f64) != f64 {
if !dc.truncate && math.Floor(f64) != f64 {
return emptyValue, errCannotTruncate
}
if f64 > float64(math.MaxInt64) {
Expand Down Expand Up @@ -368,7 +368,7 @@ func floatDecodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.

switch t.Kind() {
case reflect.Float32:
if !dc.Truncate && float64(float32(f)) != f {
if !dc.truncate && float64(float32(f)) != f {
return emptyValue, errCannotTruncate
}

Expand Down
8 changes: 4 additions & 4 deletions bson/default_value_decoders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func TestDefaultValueDecoders(t *testing.T) {
nil,
},
{
"ReadDouble (truncate)", int64(3), &DecodeContext{Truncate: true},
"ReadDouble (truncate)", int64(3), &DecodeContext{truncate: true},
&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,
nil,
},
Expand Down Expand Up @@ -419,7 +419,7 @@ func TestDefaultValueDecoders(t *testing.T) {
nil,
},
{
"ReadDouble (truncate)", uint64(3), &DecodeContext{Truncate: true},
"ReadDouble (truncate)", uint64(3), &DecodeContext{truncate: true},
&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,
nil,
},
Expand Down Expand Up @@ -670,7 +670,7 @@ func TestDefaultValueDecoders(t *testing.T) {
nil,
},
{
"float32/fast path (truncate)", float32(3.14), &DecodeContext{Truncate: true},
"float32/fast path (truncate)", float32(3.14), &DecodeContext{truncate: true},
&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,
nil,
},
Expand Down Expand Up @@ -708,7 +708,7 @@ func TestDefaultValueDecoders(t *testing.T) {
nil,
},
{
"float32/reflection path (truncate)", myfloat32(3.14), &DecodeContext{Truncate: true},
"float32/reflection path (truncate)", myfloat32(3.14), &DecodeContext{truncate: true},
&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,
nil,
},
Expand Down
2 changes: 1 addition & 1 deletion bson/default_value_encoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func intEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {
return vw.WriteInt64(i64)
case reflect.Int64:
i64 := val.Int()
if ec.MinSize && fitsIn32Bits(i64) {
if ec.minSize && fitsIn32Bits(i64) {
return vw.WriteInt32(int32(i64))
}
return vw.WriteInt64(i64)
Expand Down
Loading

0 comments on commit a5b9b2c

Please sign in to comment.