Search code examples
mongodbgobsonmongo-go

How to serialize nil pointer of structure with custom MarshalBSONValue into BSON?


mongodb official golang dirver used in my program.


package main

import (
    "fmt"
    "github.com/pkg/errors"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/bsontype"
    "go.mongodb.org/mongo-driver/x/bsonx"
    "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    "log"
    "strconv"
)

type Bar struct {
    a int64
}

func (b Bar) MarshalBSONValue() (bsontype.Type, []byte, error) {
    return bsonx.String(fmt.Sprintf("%d", b.a)).MarshalBSONValue()
}

func (b *Bar) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
    if t != bsontype.String {
        return errors.Errorf("bsontype(%s) not allowed in Bar.UnmarshalBSONValue, only string accept", t.String())
    }
    str, _, ok := bsoncore.ReadString(data)
    if !ok {
        return errors.Errorf("decode string, but string not found")
    }

    dec, err := strconv.ParseInt(str, 10, 64)
    if err != nil {
        return err
    }
    b = &Bar{a:dec}
    return nil
}

type Foo struct {
    Bar *Bar`bson:"Bar, omitempty"`
}

func main() {
    f := Foo{}
    _, err := bson.Marshal(f)
    if err != nil {
        log.Fatalf("Failed to marshal. Error: %v", err)
    }
}

this program will panic with error:

panic: value method main.Bar.MarshalBSONValue called using nil *Bar pointer

goroutine 1 [running]:
main.(*Bar).MarshalBSONValue(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        <autogenerated>:1 +0x87
reflect.Value.call(0x11ebc60, 0xc000010330, 0x293, 0x1218915, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /usr/local/go/src/reflect/value.go:447 +0x461


Solution

  • You need to remove the space before omitempty:

    type Foo struct {
        Bar *Bar`bson:"Bar,omitempty"`
    }
    

    The parser cannot handle ", omitempty", because the code is as folllows:

    for idx, str := range strings.Split(tag, ",") {
            if idx == 0 && str != "" {
                key = str
            }
            switch str {
            case "omitempty":