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?

Playground example

0

There are 0 best solutions below