Das Problem ist, dass die aktuellen bson-Codecs die Codierung/Decodierung von string
nicht unterstützen in / von null
.
Eine Möglichkeit, dies zu handhaben, besteht darin, einen benutzerdefinierten Decoder für string
zu erstellen Typ, in dem wir mit null
umgehen Werte:Wir verwenden nur die leere Zeichenkette (und, was noch wichtiger ist, melden keine Fehler).
Benutzerdefinierte Decoder werden durch den Typ bsoncodec.ValueDecoder
beschrieben . Sie können bei einer bsoncodec.Registry
registriert werden , mit einem bsoncodec.RegistryBuilder
zum Beispiel.
Registrierungen können auf mehreren Ebenen festgelegt / angewendet werden, sogar auf einen ganzen mongo.Client
, oder zu einer mongo.Database
oder einfach zu einer mongo.Collection
, beim Erwerb im Rahmen ihrer Möglichkeiten, z.B. options.ClientOptions.SetRegistry()
.
Lassen Sie uns zunächst sehen, wie wir dies für string
tun können , und als nächstes werden wir sehen, wie wir die Lösung für jeden Typ verbessern / verallgemeinern können.
1. Umgang mit null
Saiten
Das Wichtigste zuerst, lassen Sie uns einen benutzerdefinierten String-Decoder erstellen, der einen null
umwandeln kann in einen (n leeren) 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
}
OK, und jetzt sehen wir uns an, wie dieser benutzerdefinierte String-Decoder für einen mongo.Client
verwendet wird :
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(
bson.NewRegistryBuilder().
RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
Build(),
)
client, err := mongo.Connect(ctx, clientOpts)
Ab sofort mit diesem client
, wenn Sie Ergebnisse in string
dekodieren Werte, dieser registrierte nullawareStrDecoder
decoder wird aufgerufen, um die Konvertierung durchzuführen, die bson null
akzeptiert Werte und legt den leeren String ""
fest .
Aber wir können es besser machen... Lesen Sie weiter...
2. Umgang mit null
Werte beliebigen Typs:„typneutraler“ nullbewusster Decoder
Eine Möglichkeit wäre, einen separaten, benutzerdefinierten Decoder zu erstellen und ihn für jeden Typ zu registrieren, den wir handhaben möchten. Das scheint eine Menge Arbeit zu sein.
Was wir stattdessen tun können (und sollten), ist, einen einzigen, "typneutralen" benutzerdefinierten Decoder zu erstellen, der nur null
behandelt s, und wenn der BSON-Wert nicht null
ist , sollte den Standarddecoder aufrufen, um den Nicht-null
zu verarbeiten Wert.
Das ist überraschend einfach:
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
}
Wir müssen nur herausfinden, was wir für nullawareDecoder.defDecoder
verwenden . Dazu können wir die Standardregistrierung verwenden:bson.DefaultRegistry
, können wir den Standarddecoder für einzelne Typen nachschlagen. Cool.
Also registrieren wir jetzt einen Wert unseres nullawareDecoder
für alle Typen wollen wir mit null
umgehen s für. Es ist nicht so schwer. Wir listen einfach die Typen (oder Werte dieser Typen) auf, für die wir das wollen, und wir können uns mit einer einfachen Schleife um alles kümmern:
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)
Im obigen Beispiel habe ich nullfähige Decoder für string
registriert , int
und int32
, aber Sie können diese Liste nach Belieben erweitern, indem Sie einfach Werte der gewünschten Typen zu den customValues
hinzufügen Scheibe oben.