Vấn đề là codec bson hiện tại không hỗ trợ mã hóa / giải mã string
thành / từ null
.
Một cách để xử lý điều này là tạo bộ giải mã tùy chỉnh cho string
nhập vào đó chúng tôi xử lý null
giá trị:chúng tôi chỉ sử dụng chuỗi trống (và quan trọng hơn là không báo lỗi).
Bộ giải mã tùy chỉnh được mô tả bằng loại bsoncodec.ValueDecoder
. Chúng có thể được đăng ký tại bsoncodec.Registry
, sử dụng bsoncodec.RegistryBuilder
chẳng hạn.
Đăng ký có thể được đặt / áp dụng ở nhiều cấp, thậm chí cho toàn bộ mongo.Client
hoặc tới một mongo.Database
hoặc chỉ tới một mongo.Collection
, khi có được chúng, như một phần của các lựa chọn, ví dụ:options.ClientOptions.SetRegistry()
.
Trước tiên, hãy xem cách chúng ta có thể thực hiện việc này cho string
và tiếp theo, chúng ta sẽ xem cách cải thiện / tổng quát hóa giải pháp cho bất kỳ loại nào.
1. Xử lý null
chuỗi
Điều đầu tiên, hãy tạo một bộ giải mã chuỗi tùy chỉnh có thể biến null
thành một chuỗi (n rỗng):
import (
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsontype"
)
type nullawareStrDecoder struct{}
func (nullawareStrDecoder) DecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Kind() != reflect.String {
return errors.New("bad type or not settable")
}
var str string
var err error
switch vr.Type() {
case bsontype.String:
if str, err = vr.ReadString(); err != nil {
return err
}
case bsontype.Null: // THIS IS THE MISSING PIECE TO HANDLE NULL!
if err = vr.ReadNull(); err != nil {
return err
}
default:
return fmt.Errorf("cannot decode %v into a string type", vr.Type())
}
val.SetString(str)
return nil
}
OK, và bây giờ chúng ta hãy xem cách sử dụng bộ giải mã chuỗi tùy chỉnh này cho mongo.Client
:
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(
bson.NewRegistryBuilder().
RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
Build(),
)
client, err := mongo.Connect(ctx, clientOpts)
Từ bây giờ, sử dụng client
này , bất cứ khi nào bạn giải mã kết quả thành chuỗi string
giá trị, nullawareStrDecoder
đã đăng ký này bộ giải mã sẽ được gọi để xử lý chuyển đổi, chấp nhận bson null
giá trị và đặt chuỗi trống ""
.
Nhưng chúng tôi có thể làm tốt hơn ... Đọc tiếp ...
2. Xử lý null
các giá trị thuộc bất kỳ loại nào:bộ giải mã nhận biết null "loại trung tính"
Một cách sẽ là tạo một bộ giải mã tùy chỉnh, riêng biệt và đăng ký nó cho từng loại mà chúng tôi muốn xử lý. Đó dường như là rất nhiều công việc.
Thay vào đó, những gì chúng tôi có thể (và nên) làm là tạo một bộ giải mã tùy chỉnh "loại trung tính" duy nhất, chỉ xử lý null
s và nếu giá trị BSON không phải là null
, nên gọi bộ giải mã mặc định để xử lý không phải null
giá trị.
Điều này đơn giản một cách đáng ngạc nhiên:
type nullawareDecoder struct {
defDecoder bsoncodec.ValueDecoder
zeroValue reflect.Value
}
func (d *nullawareDecoder) DecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if vr.Type() != bsontype.Null {
return d.defDecoder.DecodeValue(dctx, vr, val)
}
if !val.CanSet() {
return errors.New("value not settable")
}
if err := vr.ReadNull(); err != nil {
return err
}
// Set the zero value of val's type:
val.Set(d.zeroValue)
return nil
}
Chúng tôi chỉ phải tìm ra những gì sẽ sử dụng cho nullawareDecoder.defDecoder
. Đối với điều này, chúng tôi có thể sử dụng sổ đăng ký mặc định:bson.DefaultRegistry
, chúng tôi có thể tra cứu bộ giải mã mặc định cho các loại riêng lẻ. Thật tuyệt.
Vì vậy, những gì chúng tôi làm bây giờ là đăng ký một giá trị của nullawareDecoder
của chúng tôi cho tất cả các loại mà chúng tôi muốn xử lý null
s cho. Nó không khó lắm đâu. Chúng tôi chỉ liệt kê các loại (hoặc giá trị của các loại đó) mà chúng tôi muốn điều này và chúng tôi có thể xử lý tất cả bằng một vòng lặp đơn giản:
customValues := []interface{}{
"", // string
int(0), // int
int32(0), // int32
}
rb := bson.NewRegistryBuilder()
for _, v := range customValues {
t := reflect.TypeOf(v)
defDecoder, err := bson.DefaultRegistry.LookupDecoder(t)
if err != nil {
panic(err)
}
rb.RegisterDecoder(t, &nullawareDecoder{defDecoder, reflect.Zero(t)})
}
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(rb.Build())
client, err := mongo.Connect(ctx, clientOpts)
Trong ví dụ trên, tôi đã đăng ký bộ giải mã nhận biết null cho string
, int
và int32
, nhưng bạn có thể mở rộng danh sách này theo ý thích của mình, chỉ cần thêm giá trị của các loại mong muốn vào customValues
lát trên.