Het probleem is dat de huidige bson-codecs geen ondersteuning bieden voor het coderen / decoderen van string
naar / van null
.
Een manier om dit aan te pakken is door een aangepaste decoder te maken voor string
type waarin we null
verwerken waarden:we gebruiken gewoon de lege string (en nog belangrijker, rapporteren geen fouten).
Aangepaste decoders worden beschreven door het type bsoncodec.ValueDecoder
. Ze kunnen worden geregistreerd bij een bsoncodec.Registry
, met behulp van een bsoncodec.RegistryBuilder
bijvoorbeeld.
Registries kunnen op meerdere niveaus worden ingesteld / toegepast, zelfs op een hele mongo.Client
, of naar een mongo.Database
of gewoon naar een mongo.Collection
, bij het verwerven ervan, als onderdeel van hun opties, b.v. options.ClientOptions.SetRegistry()
.
Laten we eerst eens kijken hoe we dit kunnen doen voor string
, en vervolgens zullen we zien hoe we de oplossing kunnen verbeteren / generaliseren naar elk type.
1. Omgaan met null
snaren
Laten we allereerst een aangepaste stringdecoder maken die een null
. kan veranderen in een (lege) tekenreeks:
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, en laten we nu eens kijken hoe we deze aangepaste stringdecoder kunnen gebruiken voor een mongo.Client
:
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(
bson.NewRegistryBuilder().
RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
Build(),
)
client, err := mongo.Connect(ctx, clientOpts)
Gebruik vanaf nu deze client
, telkens wanneer u resultaten decodeert in string
waarden, deze geregistreerde nullawareStrDecoder
decoder wordt aangeroepen om de conversie af te handelen, die bson null
. accepteert waarden en stelt de Ga lege tekenreeks in ""
.
Maar we kunnen beter... Lees verder...
2. Omgaan met null
waarden van elk type:"type-neutrale" null-aware decoder
Een manier zou zijn om een aparte, aangepaste decoder te maken en deze te registreren voor elk type dat we willen gebruiken. Dat lijkt veel werk te zijn.
Wat we in plaats daarvan kunnen (en moeten) doen, is een enkele, "type-neutrale" aangepaste decoder maken die alleen null
verwerkt. s, en als de BSON-waarde niet null
. is , zou de standaarddecoder moeten aanroepen om de niet-null
. te verwerken waarde.
Dit is verrassend eenvoudig:
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
}
We moeten alleen uitzoeken wat we moeten gebruiken voor nullawareDecoder.defDecoder
. Hiervoor kunnen we het standaardregister gebruiken:bson.DefaultRegistry
, kunnen we de standaarddecoder voor afzonderlijke typen opzoeken. Cool.
Dus wat we nu doen is een waarde registreren van onze nullawareDecoder
voor alle typen die we willen behandelen null
voor. Het is niet zo moeilijk. We vermelden gewoon de typen (of waarden van die typen) waarvoor we dit willen, en we kunnen voor alles zorgen met een eenvoudige lus:
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)
In het bovenstaande voorbeeld heb ik null-aware decoders geregistreerd voor string
, int
en int32
, maar u kunt deze lijst naar wens uitbreiden, voeg gewoon waarden van de gewenste typen toe aan de customValues
plak hierboven.