問題は、現在のbsonコーデックがstringのエンコード/デコードをサポートしていないことです。 nullへ/から 。
これを処理する1つの方法は、stringのカスタムデコーダーを作成することです。 nullを処理するタイプ 値:空の文字列を使用するだけです(さらに重要なことに、エラーを報告しません)。
カスタムデコーダーは、タイプbsoncodec.ValueDecoderで記述されます。 。 bsoncodec.Registryで登録できます 、bsoncodec.RegistryBuilderを使用 たとえば。
レジストリは、mongo.Client全体に対しても、複数のレベルで設定/適用できます。 、またはmongo.Database または単にmongo.Collection 、それらを取得するとき、それらのオプションの一部として、例えば。 options.ClientOptions.SetRegistry() 。
まず、stringに対してこれを行う方法を見てみましょう。 、次に、ソリューションを任意のタイプに改善/一般化する方法を説明します。
1。 nullの処理 文字列
まず最初に、nullを変換できるカスタム文字列デコーダーを作成しましょう (n個の空の)文字列に:
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
}
では、このカスタム文字列デコーダーをmongo.Clientに利用する方法を見てみましょう。 :
clientOpts := options.Client().
ApplyURI("mongodb://localhost:27017/").
SetRegistry(
bson.NewRegistryBuilder().
RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
Build(),
)
client, err := mongo.Connect(ctx, clientOpts)
今後は、このclientを使用してください 、結果をstringにデコードするときはいつでも 値、この登録されたnullawareStrDecoder デコーダーは、bson nullを受け入れる変換を処理するために呼び出されます Go empty string ""の値と設定 。
しかし、私たちはもっとうまくやることができます...続きを読む...
2。 nullの処理 任意のタイプの値:「タイプニュートラル」ヌル対応デコーダー
1つの方法は、個別のカスタムデコーダーを作成し、処理するタイプごとに登録することです。大変な作業のようです。
代わりに行うことができる(そしてすべきである)のは、nullだけを処理する単一の「タイプニュートラル」カスタムデコーダーを作成することです。 s、およびBSON値がnullでない場合 、デフォルトのデコーダーを呼び出して、null以外を処理する必要があります 値。
これは驚くほど簡単です:
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
}
nullawareDecoder.defDecoderに何を使用するかを理解する必要があります 。このために、デフォルトのレジストリを使用できます:bson.DefaultRegistry 、個々のタイプのデフォルトデコーダーを検索する場合があります。かっこいい。
したがって、ここで行うのは、nullawareDecoderの値を登録することです。 処理するすべてのタイプについてnull s。それほど難しいことではありません。これが必要なタイプ(またはそれらのタイプの値)をリストするだけで、単純なループですべてを処理できます:
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)
上記の例では、stringのnull対応デコーダーを登録しました 、int およびint32 、ただし、このリストを好みに合わせて拡張できます。必要なタイプの値をcustomValuesに追加するだけです。 上のスライス。