MongoDB Go Driver:如何将BSON null反序列化为指针类型nil(无omitempty)
问题分析与解决方案
背景代码
自定义Id类型实现BSON序列化/反序列化
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 }
结构体定义
type X struct { Id Id `bson:"id,omitempty"` Id2 *Id `bson:"id2,omitempty"` Id3 *Id `bson:"id3"` }
问题描述
序列化零值实例x := &X{}时得到BSON {id3: null},符合预期;但反序列化该BSON时,Id3被设置为指向空Id的指针(而非nil)。需解决两个问题:
- 反序列化时能否将
Id3设为nil? - 能否通过
bson.ValueUnmarshaller实现,还是只能通过结构体自定义UnmarshalBSON函数实现?
解决方案
1. 能否将Id3反序列化为nil指针?
可以,但需要通过自定义逻辑处理,无法依赖默认的反序列化行为。
2. 实现方式说明
无法仅通过修改*Id类型的bson.ValueUnmarshaller实现需求。原因在于:当反序列化*Id类型字段时,若BSON值为null,驱动会先创建一个零值Id实例,再将其地址作为*Id接收者传入UnmarshalBSONValue方法。该方法只能修改接收者指向的Id值,无法改变外层指针(结构体中的Id3字段)的指向,因此无法将其设为nil。
必须通过给结构体X实现自定义的UnmarshalBSON或UnmarshalBSONValue方法,手动处理null字段的场景,示例代码如下:
func (x *X) UnmarshalBSON(data []byte) error { // 先解析为原始BSON结构 var raw bson.Raw if err := bson.Unmarshal(data, &raw); err != nil { return err } // 处理Id字段 if err := raw.Lookup("id").Unmarshal(&x.Id); err != nil { return err } // 处理Id2字段 id2Elem := raw.Lookup("id2") if id2Elem.Type != bsontype.Null { var id2 Id if err := id2Elem.Unmarshal(&id2); err != nil { return err } x.Id2 = &id2 } else { x.Id2 = nil } // 处理Id3字段 id3Elem := raw.Lookup("id3") if id3Elem.Type != bsontype.Null { var id3 Id if err := id3Elem.Unmarshal(&id3); err != nil { return err } x.Id3 = &id3 } else { x.Id3 = nil } return nil }
此实现中,通过直接解析原始BSON,判断id3字段的类型:若为null则将x.Id3设为nil,否则反序列化为Id实例并赋值指针。
内容的提问来源于stack exchange,提问作者crunk1
相关产品推荐
相关产品推荐

