Masalahnya adalah codec bson saat ini tidak mendukung encoding / decoding string
ke / dari null
.
Salah satu cara untuk mengatasinya adalah dengan membuat dekoder khusus untuk string
ketik di mana kami menangani null
nilai:kami hanya menggunakan string kosong (dan yang lebih penting tidak melaporkan kesalahan).
Dekoder khusus dijelaskan dengan jenis bsoncodec.ValueDecoder
. Mereka dapat didaftarkan di bsoncodec.Registry
, menggunakan bsoncodec.RegistryBuilder
misalnya.
Registri dapat disetel / diterapkan pada beberapa level, bahkan ke seluruh mongo.Client
, atau ke mongo.Database
atau hanya ke mongo.Collection
, saat memperolehnya, sebagai bagian dari opsinya, mis. options.ClientOptions.SetRegistry()
.
Pertama mari kita lihat bagaimana kita bisa melakukan ini untuk string
, dan selanjutnya kita akan melihat cara meningkatkan / menggeneralisasi solusi ke semua jenis.
1. Menangani null
string
Pertama-tama, mari buat dekoder string khusus yang dapat mengubah null
menjadi a(n kosong) string:
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
}
Oke, dan sekarang mari kita lihat cara menggunakan dekoder string khusus ini ke mongo.Client
:
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(
bson.NewRegistryBuilder().
RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
Build(),
)
client, err := mongo.Connect(ctx, clientOpts)
Mulai sekarang, gunakan client
ini , setiap kali Anda mendekode hasil menjadi string
nilai, nullawareStrDecoder
ini terdaftar decoder akan dipanggil untuk menangani konversi, yang menerima bson null
nilai dan setel string kosong Go ""
.
Tapi kita bisa melakukan yang lebih baik... Baca terus...
2. Menangani null
nilai jenis apa pun:dekoder null-aware "type-neutral"
Salah satu caranya adalah dengan membuat dekoder khusus yang terpisah dan mendaftarkannya untuk setiap jenis yang ingin kita tangani. Sepertinya itu banyak pekerjaan.
Apa yang mungkin (dan harus) kami lakukan adalah membuat satu dekoder khusus "type-neutral" yang hanya menangani null
s, dan jika nilai BSON bukan null
, harus memanggil dekoder default untuk menangani non-null
nilai.
Ini sangat sederhana:
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
}
Kita hanya perlu mencari tahu apa yang akan digunakan untuk nullawareDecoder.defDecoder
. Untuk ini kami dapat menggunakan registri default:bson.DefaultRegistry
, kami mungkin mencari dekoder default untuk masing-masing tipe. Keren.
Jadi yang kita lakukan sekarang adalah mendaftarkan nilai nullawareDecoder
our untuk semua tipe yang ingin kita tangani null
untuk. Tidak sesulit itu. Kami hanya mencantumkan tipe (atau nilai dari tipe tersebut) yang kami inginkan, dan kami dapat menangani semuanya dengan loop sederhana:
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)
Pada contoh di atas saya mendaftarkan dekoder yang sadar-nol untuk string
, int
dan int32
, tetapi Anda dapat memperluas daftar ini sesuai keinginan Anda, cukup tambahkan nilai dari jenis yang diinginkan ke customValues
irisan di atas.