問題は、現在の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
に追加するだけです。 上のスライス。