Consider my custom string-type Id
which marshals/unmarshals to/from a BSON ObjectId (we abstract-away the underlying BSON at our data layer; our services are agnostic to our storage):
package id
import (
"fmt"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
type Id string
func (id *Id) MarshalBSONValue() (bsontype.Type, []byte, error) {
if id == nil {
return bsontype.Null, nil, nil
}
oid, err := primitive.ObjectIDFromHex(string(*id))
if err != nil {
return 0, nil, fmt.Errorf("cannot marshal Id value %q to BSON ObjectId: %v", *id, err)
}
return bsontype.ObjectID, bsoncore.AppendObjectID(nil, oid), nil
}
func (id *Id) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
if t == bsontype.Null {
return nil
}
if t != bsontype.ObjectID {
return fmt.Errorf("cannot unmarshal non-ObjectId BSON type %q to Id", t)
}
oid, ok := bsoncore.Value{Type: t, Data: data}.ObjectIDOK()
if !ok {
return fmt.Errorf("invalid BSON ObjectId data while unmarshalling Id, data=%v", data)
}
*id = Id(oid.Hex())
return nil
}
Now, consider a struct X
which has Id
and *Id
fields, one of which does not have "omitempty":
type X struct {
Id Id `bson:"id,omitempty"`
Id2 *Id `bson:"id2,omitempty"`
Id3 *Id `bson:"id3"`
}
As expected, marshalling a zero-value, x := &X{}
, results in the serialized BSON {id3: null}
.
However, unmarshalling that serialized BSON, {id3: null}
, into X
results with Id3
being set as a pointer to an empty Id
. When unmarshalling into pointer fields, is there a way to set Id3
to nil
? Can that be done as a bson.ValueUnmarshaller
? Or can this only be done in structs with custom UnmarshalBSON
functions?