Skip to content

Commit 7eeaa5b

Browse files
author
iwysiu
authored
GODRIVER-1423 bson should marshal nil interfaces (#243)
1 parent 0be9a84 commit 7eeaa5b

File tree

3 files changed

+93
-16
lines changed

3 files changed

+93
-16
lines changed

bson/bsoncodec/default_value_encoders.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ var defaultValueEncoders DefaultValueEncoders
2626

2727
var bvwPool = bsonrw.NewBSONValueWriterPool()
2828

29+
var errInvalidValue = errors.New("cannot encode invalid element")
30+
2931
var sliceWriterPool = sync.Pool{
3032
New: func() interface{} {
3133
sw := make(bsonrw.SliceWriter, 0, 0)
@@ -301,16 +303,24 @@ func (dve DefaultValueEncoders) mapEncodeValue(ec EncodeContext, dw bsonrw.Docum
301303
return fmt.Errorf("Key %s of inlined map conflicts with a struct field name", key)
302304
}
303305

304-
currEncoder, currVal, err := dve.lookupElementEncoder(ec, encoder, val.MapIndex(key))
305-
if err != nil {
306-
return err
306+
currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.MapIndex(key))
307+
if lookupErr != nil && lookupErr != errInvalidValue {
308+
return lookupErr
307309
}
308310

309311
vw, err := dw.WriteDocumentElement(key.String())
310312
if err != nil {
311313
return err
312314
}
313315

316+
if lookupErr == errInvalidValue {
317+
err = vw.WriteNull()
318+
if err != nil {
319+
return err
320+
}
321+
continue
322+
}
323+
314324
if enc, ok := currEncoder.(ValueEncoder); ok {
315325
err = enc.EncodeValue(ec, vw, currVal)
316326
if err != nil {
@@ -363,16 +373,24 @@ func (dve DefaultValueEncoders) ArrayEncodeValue(ec EncodeContext, vw bsonrw.Val
363373
}
364374

365375
for idx := 0; idx < val.Len(); idx++ {
366-
currEncoder, currVal, err := dve.lookupElementEncoder(ec, encoder, val.Index(idx))
367-
if err != nil {
368-
return err
376+
currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.Index(idx))
377+
if lookupErr != nil && lookupErr != errInvalidValue {
378+
return lookupErr
369379
}
370380

371381
vw, err := aw.WriteArrayElement()
372382
if err != nil {
373383
return err
374384
}
375385

386+
if lookupErr == errInvalidValue {
387+
err = vw.WriteNull()
388+
if err != nil {
389+
return err
390+
}
391+
continue
392+
}
393+
376394
err = currEncoder.EncodeValue(ec, vw, currVal)
377395
if err != nil {
378396
return err
@@ -422,16 +440,24 @@ func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.Val
422440
}
423441

424442
for idx := 0; idx < val.Len(); idx++ {
425-
currEncoder, currVal, err := dve.lookupElementEncoder(ec, encoder, val.Index(idx))
426-
if err != nil {
427-
return err
443+
currEncoder, currVal, lookupErr := dve.lookupElementEncoder(ec, encoder, val.Index(idx))
444+
if lookupErr != nil && lookupErr != errInvalidValue {
445+
return lookupErr
428446
}
429447

430448
vw, err := aw.WriteArrayElement()
431449
if err != nil {
432450
return err
433451
}
434452

453+
if lookupErr == errInvalidValue {
454+
err = vw.WriteNull()
455+
if err != nil {
456+
return err
457+
}
458+
continue
459+
}
460+
435461
err = currEncoder.EncodeValue(ec, vw, currVal)
436462
if err != nil {
437463
return err
@@ -446,7 +472,7 @@ func (dve DefaultValueEncoders) lookupElementEncoder(ec EncodeContext, origEncod
446472
}
447473
currVal = currVal.Elem()
448474
if !currVal.IsValid() {
449-
return nil, currVal, fmt.Errorf("cannot encode invalid element")
475+
return nil, currVal, errInvalidValue
450476
}
451477
currEncoder, err := ec.LookupEncoder(currVal.Type())
452478

bson/bsoncodec/default_value_encoders_test.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,11 @@ func TestDefaultValueEncoders(t *testing.T) {
249249
},
250250
{
251251
"Lookup Error",
252-
map[string]interface{}{"foo": nil},
252+
map[string]int{"foo": 1},
253253
&EncodeContext{Registry: NewRegistryBuilder().Build()},
254254
&bsonrwtest.ValueReaderWriter{},
255255
bsonrwtest.WriteDocument,
256-
fmt.Errorf("cannot encode invalid element"),
256+
fmt.Errorf("no encoder found for int"),
257257
},
258258
{
259259
"WriteDocumentElement Error",
@@ -287,6 +287,14 @@ func TestDefaultValueEncoders(t *testing.T) {
287287
bsonrwtest.WriteDocumentEnd,
288288
nil,
289289
},
290+
{
291+
"with interface/nil/success",
292+
map[string]myInterface{"foo": nil},
293+
&EncodeContext{Registry: buildDefaultRegistry()},
294+
nil,
295+
bsonrwtest.WriteDocumentEnd,
296+
nil,
297+
},
290298
},
291299
},
292300
{
@@ -311,11 +319,11 @@ func TestDefaultValueEncoders(t *testing.T) {
311319
},
312320
{
313321
"Lookup Error",
314-
[1]interface{}{},
322+
[1]int{1},
315323
&EncodeContext{Registry: NewRegistryBuilder().Build()},
316324
&bsonrwtest.ValueReaderWriter{},
317325
bsonrwtest.WriteArray,
318-
fmt.Errorf("cannot encode invalid element"),
326+
fmt.Errorf("no encoder found for int"),
319327
},
320328
{
321329
"WriteArrayElement Error",
@@ -357,6 +365,14 @@ func TestDefaultValueEncoders(t *testing.T) {
357365
bsonrwtest.WriteArrayEnd,
358366
nil,
359367
},
368+
{
369+
"[1]interface/nil/success",
370+
[1]myInterface{nil},
371+
&EncodeContext{Registry: buildDefaultRegistry()},
372+
nil,
373+
bsonrwtest.WriteArrayEnd,
374+
nil,
375+
},
360376
},
361377
},
362378
{
@@ -381,11 +397,11 @@ func TestDefaultValueEncoders(t *testing.T) {
381397
},
382398
{
383399
"Lookup Error",
384-
[]interface{}{nil},
400+
[]int{1},
385401
&EncodeContext{Registry: NewRegistryBuilder().Build()},
386402
&bsonrwtest.ValueReaderWriter{},
387403
bsonrwtest.WriteArray,
388-
fmt.Errorf("cannot encode invalid element"),
404+
fmt.Errorf("no encoder found for int"),
389405
},
390406
{
391407
"WriteArrayElement Error",
@@ -435,6 +451,14 @@ func TestDefaultValueEncoders(t *testing.T) {
435451
bsonrwtest.WriteArrayEnd,
436452
nil,
437453
},
454+
{
455+
"interface/success",
456+
[]myInterface{nil},
457+
&EncodeContext{Registry: buildDefaultRegistry()},
458+
nil,
459+
bsonrwtest.WriteArrayEnd,
460+
nil,
461+
},
438462
},
439463
},
440464
{
@@ -935,6 +959,14 @@ func TestDefaultValueEncoders(t *testing.T) {
935959
bsonrwtest.WriteDocumentEnd,
936960
nil,
937961
},
962+
{
963+
"nil interface value",
964+
struct{ Foo myInterface }{Foo: nil},
965+
&EncodeContext{Registry: buildDefaultRegistry()},
966+
nil,
967+
bsonrwtest.WriteDocumentEnd,
968+
nil,
969+
},
938970
},
939971
},
940972
{

bson/bsoncodec/struct_codec.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,25 @@ func (sc *StructCodec) EncodeValue(r EncodeContext, vw bsonrw.ValueWriter, val r
9898

9999
desc.encoder, rv, err = defaultValueEncoders.lookupElementEncoder(r, desc.encoder, rv)
100100

101+
if err != nil && err != errInvalidValue {
102+
return err
103+
}
104+
105+
if err == errInvalidValue {
106+
if desc.omitEmpty {
107+
continue
108+
}
109+
vw2, err := dw.WriteDocumentElement(desc.name)
110+
if err != nil {
111+
return err
112+
}
113+
err = vw2.WriteNull()
114+
if err != nil {
115+
return err
116+
}
117+
continue
118+
}
119+
101120
if desc.encoder == nil {
102121
return ErrNoEncoder{Type: rv.Type()}
103122
}

0 commit comments

Comments
 (0)