web-dev-qa-db-ja.com

C#MongoDB複合クラスのシリアル化

私はC#MongoDBドライバーを使用しており、保存するためにこのかなり複雑なJSON構造体があります。

{
    "name" : "value",
    "age": 1,
    "isFemale": true,
    "Hobbies" : {
        //All data within the "Hobbies" node is dynamic
        //and may change from one item to another.
        "stringItem" : "value",
        "intItem" : 0.0,
        "listOfItems" : [
            { "field" : 1696.0 }
        ],
        "intArray" : [ 566.0,  1200.0 ]
    },
    "Collection" : [ 
        //All data within the "Collection" node is dynamic
        //and may change from one item to another.
        {
            "field" : "string",
            "FieldTypeId" : 2.0,
            "array" : [ 
                { "value" : "1024x1000" }
            ]
        }
    ]
}

上記のオブジェクトを表すこのクラスがあります。

public class MyClass
{
    public string Name;
    public int Age;
    public bool IsFemale;
    public Dictionary<string, object> Hobbies;
    public List<Dictionary<string, object>> Collection;
}

「趣味」セクションで保存されるものは次のとおりです。

"Hobbies": {
    "stringItem": "value",
    "intItem": 1,
    "listOfItems": {
        "_t": "Newtonsoft.Json.Linq.JArray, Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed",
        "_v": [{
            "_t": "JObject",
            "_v": [{
                "_t": "JProperty",
                "_v": [{
                    "_t": "JValue",
                    "_v": []
                }]
            },
            {
                "_t": "JProperty",
                "_v": [{
                    "_t": "JValue",
                    "_v": []
                }]
            }]
        }]
    }
}

私の推測では、そのクラスのカスタムシリアライザーを作成する必要がありますが、 MONGODB .NET DRIVER Manual に有用な例が見つかりませんでした。

私は次のようなものを開始して実装しようとしました:

public class MyClassSerializer : SerializerBase<MyClass>, IBsonDocumentSerializer
{
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, MyClass value)
    {
        //What to do here???
        base.Serialize(context, args, value);
    }

    public override MyClass Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        //What to do here???
        return base.Deserialize(context, args);
    }

    public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
    {
        switch (memberName)
        {
            case "Name":
                serializationInfo = new BsonSerializationInfo(memberName, new StringSerializer(), typeof(string));
                return true;
            case "Age":
                serializationInfo = new BsonSerializationInfo(memberName, new Int16Serializer(), typeof(string));
                return true;
            case "IsFemale":
                serializationInfo = new BsonSerializationInfo(memberName, new BooleanSerializer(), typeof(string));
                return true;
            case "Hobbies":
                serializationInfo = new BsonSerializationInfo(memberName, new TupleSerializer<string, object>(), typeof(string));
                return true;
            case "Collection":
                serializationInfo = new BsonSerializationInfo(memberName, new ArraySerializer<Dictionary<string, object>>(), typeof(string));
                return true;
            default:
                serializationInfo = null;
                return false;
        }
    }
}

しかし、私はこれとは何の関係も見つけることができませんでした。完全には実装されておらず、どこかにシリアライザーを登録する必要があると読みましたが、どこで...?

5
Liran Friedman

ここでデータをMongoDBに保存する方法を見つけました: _ tフィールドを省略した辞書からBsonDocumentへの変換 そしてそれを少し拡張したので、完全なソリューションを共有しようと思いました。

ステップ#1:

私のクラスでは、値ごとに2つのメンバーを宣言しました。

// For the Hobbies object type:
[BsonIgnore] //ignore this value in MongoDB
public Dictionary<string, object> Hobbies { get; set; }

[JsonIgnore] //ignore this value in the response on Get requests
[BsonElement(elementName: "Hobbies")]
public BsonDocument HobbiesBson { get; set; }

/*********************************************************************/

// For the Collection object type:
[BsonIgnore] //ignore this value in MongoDB
public List<Dictionary<string, object>> Collection { get; set; }

[JsonIgnore] //ignore this value in the response on Get requests
[BsonElement(elementName: "Collection")]
public BsonArray CollectionBson { get; set; }

ステップ#2

PostのWebAPIコントローラーメソッドで

[HttpPost]
public override async Task<IActionResult> Post([FromBody] Person person)
{
    var jsonDoc = JsonConvert.SerializeObject(person.Hobbies);
    person.HobbiesBson = BsonSerializer.Deserialize<BsonDocument>(jsonDoc);

    jsonDoc = JsonConvert.SerializeObject(person.Collection);
    person.CollectionBson = BsonSerializer.Deserialize<BsonArray>(jsonDoc);

    //save
}

ステップ#3

Getリクエストで、次のように逆シリアル化します。

[HttpGet("{id?}")]
public override async Task<IActionResult> Get(string id = null)
{
    var people = //get data from mongoDB
    foreach (var person in people)
    {
        var bsonDoc = BsonExtensionMethods.ToJson(person.HobbiesBson);
        person.Hobbies = JsonConvert.DeserializeObject<Dictionary<string, object>>(bsonDoc);

        bsonDoc = BsonExtensionMethods.ToJson(person.CollectionBson);
        person.Collection = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(bsonDoc);bsonDoc);
    }
    return Ok(people);
}

これで私の問題は解決し、他の人にも役立つことを願っています:-)

7
Liran Friedman

この問題に対処し、追加のプロパティを作成する必要がない簡単な方法は、カスタムシリアライザーを登録することです。

ComplexTypeSerializer.cs

namespace MyNamespace.MongoDB.Serializers
{
    public class ComplexTypeSerializer : SerializerBase<object>
    {
        public override object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
        {
            var serializer = BsonSerializer.LookupSerializer(typeof(BsonDocument));
            var document = serializer.Deserialize(context, args);

            var bsonDocument = document.ToBsonDocument();

            var result = BsonExtensionMethods.ToJson(bsonDocument);
            return JsonConvert.DeserializeObject<IDictionary<string, object>>(result);
        }

        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
        {
            var jsonDocument = JsonConvert.SerializeObject(value);
            var bsonDocument = BsonSerializer.Deserialize<BsonDocument>(jsonDocument);

            var serializer = BsonSerializer.LookupSerializer(typeof(BsonDocument));
            serializer.Serialize(context, bsonDocument.AsBsonValue);
        }
    }
}

そして、それを次のように登録します http://mongodb.github.io/mongo-csharp-driver/2.3/reference/bson/serialization/

BsonSerializer.RegisterSerializer(typeof(IDictionary<string, object>), new ComplexTypeSerializer());

または

BsonSerializer.RegisterSerializer(typeof(IList<IDictionary<string, object>>), new ComplexTypeSerializer());

以下のコードは、単純なIDictionary<string, object>でテストされており、IList<IDictionary<string, object>>で機能するかどうかはわかりませんが、サポートされていない場合は、別のカスタムシリアライザーを作成してサポートできます。

4
Alex.H

Serializer Registryセクション( http://mongodb.github.io/mongo-csharp-driver/2.4/reference/bson/serialization/#serializer-registry )内で、ドキュメントをより注意深く読んでみてください。 :

シリアライザレジストリには、登録されているすべてのIBsonSerializerが含まれています。静的クラスBsonSerializerのSerializerRegistryプロパティを介してアクセスできます。

シリアライザーを登録するには、次のいずれかを使用します

BsonSerializer.RegisterSerializer(Type, IBsonSerializer)    

または

BsonSerializer.RegisterSerializer<T>(IBsonSerializer<T>)

これはそれらをに追加します

BsonSerializer.SerializerRegistry

ここでこれらのクラスのAPIを確認してください--- http://mongodb.github.io/mongo-csharp-driver/2.4/apidocs/html/T_MongoDB_Bson_Serialization_BsonSerializer.htm

0
Kevin Smith