Skip to content

Commit ed1cef6

Browse files
author
Divjot Arora
authored
GODRIVER-1466 Respect type map entries for bsontype.EmbeddedDocument (#281)
1 parent 9bf2a79 commit ed1cef6

File tree

6 files changed

+133
-40
lines changed

6 files changed

+133
-40
lines changed

bson/bsoncodec/default_value_decoders.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func (dvd DefaultValueDecoders) RegisterDefaultDecoders(rb *RegistryBuilder) {
9999
RegisterTypeMapEntry(bsontype.MinKey, tMinKey).
100100
RegisterTypeMapEntry(bsontype.MaxKey, tMaxKey).
101101
RegisterTypeMapEntry(bsontype.Type(0), tD).
102+
RegisterTypeMapEntry(bsontype.EmbeddedDocument, tD).
102103
RegisterHookDecoder(tValueUnmarshaler, ValueDecoderFunc(dvd.ValueUnmarshalerDecodeValue)).
103104
RegisterHookDecoder(tUnmarshaler, ValueDecoderFunc(dvd.UnmarshalerDecodeValue))
104105
}

bson/bsoncodec/default_value_decoders_test.go

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,7 +2660,7 @@ func TestDefaultValueDecoders(t *testing.T) {
26602660
})
26612661
})
26622662

2663-
t.Run("EmptyInterfaceDecodeValue", func(t *testing.T) {
2663+
t.Run("defaultEmptyInterfaceCodec", func(t *testing.T) {
26642664
t.Run("DecodeValue", func(t *testing.T) {
26652665
testCases := []struct {
26662666
name string
@@ -2777,7 +2777,7 @@ func TestDefaultValueDecoders(t *testing.T) {
27772777
val := reflect.New(tEmpty).Elem()
27782778
dc := DecodeContext{Registry: NewRegistryBuilder().Build()}
27792779
want := ErrNoTypeMapEntry{Type: tc.bsontype}
2780-
got := dvd.EmptyInterfaceDecodeValue(dc, llvr, val)
2780+
got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, val)
27812781
if !compareErrors(got, want) {
27822782
t.Errorf("Errors are not equal. got %v; want %v", got, want)
27832783
}
@@ -2794,7 +2794,7 @@ func TestDefaultValueDecoders(t *testing.T) {
27942794
Build(),
27952795
}
27962796
want := ErrNoDecoder{Type: reflect.TypeOf(tc.val)}
2797-
got := dvd.EmptyInterfaceDecodeValue(dc, llvr, val)
2797+
got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, val)
27982798
if !compareErrors(got, want) {
27992799
t.Errorf("Errors are not equal. got %v; want %v", got, want)
28002800
}
@@ -2812,7 +2812,7 @@ func TestDefaultValueDecoders(t *testing.T) {
28122812
RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
28132813
Build(),
28142814
}
2815-
got := dvd.EmptyInterfaceDecodeValue(dc, llvr, reflect.New(tEmpty).Elem())
2815+
got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, reflect.New(tEmpty).Elem())
28162816
if !compareErrors(got, want) {
28172817
t.Errorf("Errors are not equal. got %v; want %v", got, want)
28182818
}
@@ -2828,7 +2828,7 @@ func TestDefaultValueDecoders(t *testing.T) {
28282828
Build(),
28292829
}
28302830
got := reflect.New(tEmpty).Elem()
2831-
err := dvd.EmptyInterfaceDecodeValue(dc, llvr, got)
2831+
err := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, got)
28322832
noerr(t, err)
28332833
if !cmp.Equal(got.Interface(), want, cmp.Comparer(compareDecimal128)) {
28342834
t.Errorf("Did not receive expected value. got %v; want %v", got.Interface(), want)
@@ -2841,7 +2841,7 @@ func TestDefaultValueDecoders(t *testing.T) {
28412841
t.Run("non-interface{}", func(t *testing.T) {
28422842
val := uint64(1234567890)
28432843
want := ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}
2844-
got := dvd.EmptyInterfaceDecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
2844+
got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
28452845
if !compareErrors(got, want) {
28462846
t.Errorf("Errors are not equal. got %v; want %v", got, want)
28472847
}
@@ -2850,7 +2850,7 @@ func TestDefaultValueDecoders(t *testing.T) {
28502850
t.Run("nil *interface{}", func(t *testing.T) {
28512851
var val interface{}
28522852
want := ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}
2853-
got := dvd.EmptyInterfaceDecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
2853+
got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
28542854
if !compareErrors(got, want) {
28552855
t.Errorf("Errors are not equal. got %v; want %v", got, want)
28562856
}
@@ -2860,7 +2860,7 @@ func TestDefaultValueDecoders(t *testing.T) {
28602860
llvr := &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double}
28612861
want := ErrNoTypeMapEntry{Type: bsontype.Double}
28622862
val := reflect.New(tEmpty).Elem()
2863-
got := dvd.EmptyInterfaceDecodeValue(DecodeContext{Registry: NewRegistryBuilder().Build()}, llvr, val)
2863+
got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: NewRegistryBuilder().Build()}, llvr, val)
28642864
if !compareErrors(got, want) {
28652865
t.Errorf("Errors are not equal. got %v; want %v", got, want)
28662866
}
@@ -2871,12 +2871,94 @@ func TestDefaultValueDecoders(t *testing.T) {
28712871
want := primitive.D{{"pi", 3.14159}}
28722872
var got interface{}
28732873
val := reflect.ValueOf(&got).Elem()
2874-
err := dvd.EmptyInterfaceDecodeValue(DecodeContext{Registry: buildDefaultRegistry()}, vr, val)
2874+
err := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: buildDefaultRegistry()}, vr, val)
28752875
noerr(t, err)
28762876
if !cmp.Equal(got, want) {
28772877
t.Errorf("Did not get correct result. got %v; want %v", got, want)
28782878
}
28792879
})
2880+
t.Run("custom type map entry", func(t *testing.T) {
2881+
// registering a custom type map entry for both bsontype.Type(0) anad bsontype.EmbeddedDocument should cause
2882+
// both top-level and embedded documents to decode to registered type when unmarshalling to interface{}
2883+
2884+
topLevelRb := NewRegistryBuilder()
2885+
defaultValueEncoders.RegisterDefaultEncoders(topLevelRb)
2886+
defaultValueDecoders.RegisterDefaultDecoders(topLevelRb)
2887+
topLevelRb.RegisterTypeMapEntry(bsontype.Type(0), reflect.TypeOf(primitive.M{}))
2888+
2889+
embeddedRb := NewRegistryBuilder()
2890+
defaultValueEncoders.RegisterDefaultEncoders(embeddedRb)
2891+
defaultValueDecoders.RegisterDefaultDecoders(embeddedRb)
2892+
embeddedRb.RegisterTypeMapEntry(bsontype.Type(0), reflect.TypeOf(primitive.M{}))
2893+
2894+
// create doc {"nested": {"foo": 1}}
2895+
innerDoc := bsoncore.BuildDocument(
2896+
nil,
2897+
bsoncore.AppendInt32Element(nil, "foo", 1),
2898+
)
2899+
doc := bsoncore.BuildDocument(
2900+
nil,
2901+
bsoncore.AppendDocumentElement(nil, "nested", innerDoc),
2902+
)
2903+
want := primitive.M{
2904+
"nested": primitive.M{
2905+
"foo": int32(1),
2906+
},
2907+
}
2908+
2909+
testCases := []struct {
2910+
name string
2911+
registry *Registry
2912+
}{
2913+
{"top level", topLevelRb.Build()},
2914+
{"embedded", embeddedRb.Build()},
2915+
}
2916+
for _, tc := range testCases {
2917+
var got interface{}
2918+
vr := bsonrw.NewBSONDocumentReader(doc)
2919+
val := reflect.ValueOf(&got).Elem()
2920+
2921+
err := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: tc.registry}, vr, val)
2922+
noerr(t, err)
2923+
if !cmp.Equal(got, want) {
2924+
t.Fatalf("got %v, want %v", got, want)
2925+
}
2926+
}
2927+
})
2928+
t.Run("ancestor info is used over custom type map entry", func(t *testing.T) {
2929+
// If a type map entry is registered for bsontype.EmbeddedDocument, the decoder should use ancestor
2930+
// information if available instead of the registered entry.
2931+
2932+
rb := NewRegistryBuilder()
2933+
defaultValueEncoders.RegisterDefaultEncoders(rb)
2934+
defaultValueDecoders.RegisterDefaultDecoders(rb)
2935+
rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(primitive.M{}))
2936+
reg := rb.Build()
2937+
2938+
// build document {"nested": {"foo": 10}}
2939+
inner := bsoncore.BuildDocument(
2940+
nil,
2941+
bsoncore.AppendInt32Element(nil, "foo", 10),
2942+
)
2943+
doc := bsoncore.BuildDocument(
2944+
nil,
2945+
bsoncore.AppendDocumentElement(nil, "nested", inner),
2946+
)
2947+
want := primitive.D{
2948+
{"nested", primitive.D{
2949+
{"foo", int32(10)},
2950+
}},
2951+
}
2952+
2953+
var got primitive.D
2954+
vr := bsonrw.NewBSONDocumentReader(doc)
2955+
val := reflect.ValueOf(&got).Elem()
2956+
err := defaultSliceCodec.DecodeValue(DecodeContext{Registry: reg}, vr, val)
2957+
noerr(t, err)
2958+
if !cmp.Equal(got, want) {
2959+
t.Fatalf("got %v, want %v", got, want)
2960+
}
2961+
})
28802962
})
28812963
}
28822964

bson/bsoncodec/empty_interface_codec.go

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ var defaultEmptyInterfaceCodec = NewEmptyInterfaceCodec()
1919

2020
// EmptyInterfaceCodec is the Codec used for interface{} values.
2121
type EmptyInterfaceCodec struct {
22-
DecodeAsMap bool
2322
DecodeBinaryAsSlice bool
2423
}
2524

@@ -30,9 +29,6 @@ func NewEmptyInterfaceCodec(opts ...*bsonoptions.EmptyInterfaceCodecOptions) *Em
3029
interfaceOpt := bsonoptions.MergeEmptyInterfaceCodecOptions(opts...)
3130

3231
codec := EmptyInterfaceCodec{}
33-
if interfaceOpt.DecodeAsMap != nil {
34-
codec.DecodeAsMap = *interfaceOpt.DecodeAsMap
35-
}
3632
if interfaceOpt.DecodeBinaryAsSlice != nil {
3733
codec.DecodeBinaryAsSlice = *interfaceOpt.DecodeBinaryAsSlice
3834
}
@@ -56,24 +52,49 @@ func (eic EmptyInterfaceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWrit
5652
return encoder.EncodeValue(ec, vw, val.Elem())
5753
}
5854

55+
func (eic EmptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, valueType bsontype.Type) (reflect.Type, error) {
56+
isDocument := valueType == bsontype.Type(0) || valueType == bsontype.EmbeddedDocument
57+
if isDocument && dc.Ancestor != nil {
58+
// Using ancestor information rather than looking up the type map entry forces consistent decoding.
59+
// If we're decoding into a bson.D, subdocuments should also be decoded as bson.D, even if a type map entry
60+
// has been registered.
61+
return dc.Ancestor, nil
62+
}
63+
64+
rtype, err := dc.LookupTypeMapEntry(valueType)
65+
if err == nil {
66+
return rtype, nil
67+
}
68+
69+
if isDocument {
70+
// For documents, fallback to looking up a type map entry for bsontype.Type(0) or bsontype.EmbeddedDocument,
71+
// depending on the original valueType.
72+
var lookupType bsontype.Type
73+
switch valueType {
74+
case bsontype.Type(0):
75+
lookupType = bsontype.EmbeddedDocument
76+
case bsontype.EmbeddedDocument:
77+
lookupType = bsontype.Type(0)
78+
}
79+
80+
rtype, err = dc.LookupTypeMapEntry(lookupType)
81+
if err == nil {
82+
return rtype, nil
83+
}
84+
}
85+
86+
return nil, err
87+
}
88+
5989
// DecodeValue is the ValueDecoderFunc for interface{}.
6090
func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
6191
if !val.CanSet() || val.Type() != tEmpty {
6292
return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val}
6393
}
6494

65-
rtype, err := dc.LookupTypeMapEntry(vr.Type())
95+
rtype, err := eic.getEmptyInterfaceDecodeType(dc, vr.Type())
6696
if err != nil {
6797
switch vr.Type() {
68-
case bsontype.EmbeddedDocument:
69-
if dc.Ancestor != nil {
70-
rtype = dc.Ancestor
71-
break
72-
}
73-
rtype = tD
74-
if eic.DecodeAsMap {
75-
rtype = tM
76-
}
7798
case bsontype.Null:
7899
val.Set(reflect.Zero(val.Type()))
79100
return vr.ReadNull()

bson/bsoncodec/registry.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,10 @@ func (rb *RegistryBuilder) RegisterDefaultDecoder(kind reflect.Kind, dec ValueDe
255255
// mapping is decoding situations where an empty interface is used and a default type needs to be
256256
// created and decoded into.
257257
//
258-
// NOTE: It is unlikely that registering a type for BSON Embedded Document is actually desired. By
259-
// registering a type map entry for BSON Embedded Document the type registered will be used in any
260-
// case where a BSON Embedded Document will be decoded into an empty interface. For example, if you
261-
// register primitive.M, the EmptyInterface decoder will always use primitive.M, even if an ancestor
262-
// was a primitive.D.
258+
// By default, BSON documents will decode into interface{} values as bson.D. To change the default type for BSON
259+
// documents, a type map entry for bsontype.EmbeddedDocument should be registered. For example, to force BSON documents
260+
// to decode to bson.Raw, use the following code:
261+
// rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(bson.Raw{}))
263262
func (rb *RegistryBuilder) RegisterTypeMapEntry(bt bsontype.Type, rt reflect.Type) *RegistryBuilder {
264263
rb.typeMap[bt] = rt
265264
return rb

bson/bsonoptions/empty_interface_codec_options.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package bsonoptions
88

99
// EmptyInterfaceCodecOptions represents all possible options for interface{} encoding and decoding.
1010
type EmptyInterfaceCodecOptions struct {
11-
DecodeAsMap *bool // Specifies if the default type for decoding should be a bson.M instead of a bson.D. Defaults to false.
1211
DecodeBinaryAsSlice *bool // Specifies if Old and Generic type binarys should default to []slice instead of primitive.Binary. Defaults to false.
1312
}
1413

@@ -17,12 +16,6 @@ func EmptyInterfaceCodec() *EmptyInterfaceCodecOptions {
1716
return &EmptyInterfaceCodecOptions{}
1817
}
1918

20-
// SetDecodeAsMap specifies if the default type for decoding should be a bson.M instead of a bson.D. Defaults to false.
21-
func (e *EmptyInterfaceCodecOptions) SetDecodeAsMap(t bool) *EmptyInterfaceCodecOptions {
22-
e.DecodeAsMap = &t
23-
return e
24-
}
25-
2619
// SetDecodeBinaryAsSlice specifies if Old and Generic type binarys should default to []slice instead of primitive.Binary. Defaults to false.
2720
func (e *EmptyInterfaceCodecOptions) SetDecodeBinaryAsSlice(b bool) *EmptyInterfaceCodecOptions {
2821
e.DecodeBinaryAsSlice = &b
@@ -36,9 +29,6 @@ func MergeEmptyInterfaceCodecOptions(opts ...*EmptyInterfaceCodecOptions) *Empty
3629
if opt == nil {
3730
continue
3831
}
39-
if opt.DecodeAsMap != nil {
40-
e.DecodeAsMap = opt.DecodeAsMap
41-
}
4232
if opt.DecodeBinaryAsSlice != nil {
4333
e.DecodeBinaryAsSlice = opt.DecodeBinaryAsSlice
4434
}

bson/mgocompat/registry.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ func newMgoRegistryBuilder() *bsoncodec.RegistryBuilder {
5555
SetAllowUnexportedFields(true))
5656
emptyInterCodec := bsoncodec.NewEmptyInterfaceCodec(
5757
bsonoptions.EmptyInterfaceCodec().
58-
SetDecodeAsMap(true).
5958
SetDecodeBinaryAsSlice(true))
6059
mapCodec := bsoncodec.NewMapCodec(
6160
bsonoptions.MapCodec().
@@ -77,9 +76,10 @@ func newMgoRegistryBuilder() *bsoncodec.RegistryBuilder {
7776
RegisterDefaultEncoder(reflect.Uint32, uintcodec).
7877
RegisterDefaultEncoder(reflect.Uint64, uintcodec).
7978
RegisterTypeMapEntry(bsontype.Int32, tInt).
80-
RegisterTypeMapEntry(bsontype.Type(0), tM).
8179
RegisterTypeMapEntry(bsontype.DateTime, tTime).
8280
RegisterTypeMapEntry(bsontype.Array, tInterfaceSlice).
81+
RegisterTypeMapEntry(bsontype.Type(0), tM).
82+
RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).
8383
RegisterHookEncoder(tGetter, bsoncodec.ValueEncoderFunc(GetterEncodeValue)).
8484
RegisterHookDecoder(tSetter, bsoncodec.ValueDecoderFunc(SetterDecodeValue))
8585

0 commit comments

Comments
 (0)