首页 > 解决方案 > 如何为字典中的值创建自定义序列化程序?

问题描述

我有以下课程:

 public class Farm
 {
   public string County {get;set;}
   
   public Dictionary<string, object> FarmItems {get;set;}
 }

我需要实现一个自定义序列化器/反序列化器,如果它是值中的整数,则将其作为字符串存储在数据库中,然后将其反序列化回整数

我查看了文档,他们详细说明了如何为单个值而不是字典https://mongodb.github.io/mongo-csharp-driver/2.12/reference/bson/serialization/

此外,文档没有提到如何实现它,我只是在创建我的类之后添加一个属性,如下所示:

[BsonSerializer(typeof(MyCustomSerializer))]
public Dictionary<string, object> FarmItems {get;set;}

标签: c#mongodbbson

解决方案


干得好:

public class MyDictionarySerialzer : SerializerBase<Dictionary<string, object>>
{
    public override void Serialize(BsonSerializationContext ctx, BsonSerializationArgs args, Dictionary<string, object> dictionary)
    {
        if (dictionary == null)
        {
            ctx.Writer.WriteStartArray();
            ctx.Writer.WriteEndArray();
            return;
        }

        ctx.Writer.WriteStartArray();

        foreach (var kvPair in dictionary)
        {
            ctx.Writer.WriteStartDocument();

            ctx.Writer.WriteName("k");
            ctx.Writer.WriteString(kvPair.Key);

            ctx.Writer.WriteName("v");
            if (kvPair.Value is int)
                ctx.Writer.WriteString(kvPair.Value.ToString());
            else
                BsonSerializer.Serialize(ctx.Writer, kvPair.Value);

            ctx.Writer.WriteEndDocument();
        }

        ctx.Writer.WriteEndArray();
    }

    public override Dictionary<string, object> Deserialize(BsonDeserializationContext ctx, BsonDeserializationArgs args)
    {
        Dictionary<string, object> dict = new();

        switch (ctx.Reader.CurrentBsonType)
        {
            case BsonType.Array:
                foreach (var val in BsonSerializer.Deserialize<BsonArray>(ctx.Reader))
                {
                    string key = val["k"].AsString;
                    object value = BsonSerializer.Deserialize<object>(val["v"].ToJson());

                    if (int.TryParse(value?.ToString(), out var parsedInt))
                        value = parsedInt;

                    dict.Add(key, value);
                }
                return dict;

            default:
                throw new BsonSerializationException("Unable to deserialize dictionary!");
        }
    }
}

如果您在字典值中存储任何复杂实体,则必须像这样使用 mongo 驱动程序注册(在应用程序启动时)该复杂类型(否则反序列化将失败):

BsonClassMap.RegisterClassMap<ComplexItem>();

像这样简单地装饰属性:

public class Farm
{
    [BsonSerializer(typeof(MyDictionarySerialzer))]
    public Dictionary<string, object> FarmItems { get; set; }
}

示例实体:

var example = new Farm
{
    FarmItems = new Dictionary<string, object>()
    {
        {"empty",null },
        {"string","test string" },
        {"int",100 },
        {"complex",new ComplexItem{ Age = 10, Name = "test" } }
    }
};

它将像这样序列化到数据库:

{
    "FarmItems" : [
        {
            "k" : "empty",
            "v" : null
        },
        {
            "k" : "string",
            "v" : "test string"
        },
        {
            "k" : "int",
            "v" : "100"
        },
        {
            "k" : "complex",
            "v" : {
                "_t" : "ComplexItem",
                "Name" : "test",
                "Age" : 10
            }
        }
    ]
}

推荐阅读