Mongo自定义序列化程序导致无法创建抽象类

Posted

技术标签:

【中文标题】Mongo自定义序列化程序导致无法创建抽象类【英文标题】:Mongo Custom serializer causing Cannot create an abstract class 【发布时间】:2021-10-29 01:06:59 【问题描述】:

Mongo 自定义序列化程序导致无法创建抽象类

您好,我正在尝试将自定义序列化程序添加到我的 mongo 类中,但它似乎无法正常工作。 目标是我们在数据库中有古代事件,我想以我们拥有的“新格式”来阅读它们。

但是新事件正在发生错误。这是结构

public abastract class FooBase

    public int Version  get; set; 
    public Guid AggregateId  get; set; 
    //Other properties
    public abstract object GetContent();


public class TypedFooBase<T> : FooBase
where T : class

    public T TypedContent  get; set; 
    //Other properties
    
    public override object GetContent()
    
        return TypedContent;
    

然后我创建了一个自定义序列化程序,如下所示:

public class BsonAncientEventSerializer<T> : SerializerBase<TypedFooBase<T>> where T : class

    public override TypedFooBase<T> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    
        var serializer = BsonSerializer.LookupSerializer(typeof(ExpandoObject));
        var obj = serializer.Deserialize(context);
        string valueString = JsonConvert.SerializeObject(obj);
        return JsonConvert.DeserializeObject<TypedFooBase<T>>(valueString, new Json.JsonAncientEventSerializer<T>())
                ?? throw new Exception("something went wrong");
    

    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TypedFooBase<T> value)
    
        var bsonWriter = context.Writer;

        var fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); 
        var propsAll = value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 

        var props = new List<PropertyInfo>(); 
        foreach (var prop in propsAll) 
         
            if (prop.CanWrite) 
             
                props.Add(prop); 
             
         

        bsonWriter.WriteStartDocument(); 

        foreach (var field in fields) 
         
            bsonWriter.WriteName(field.Name); 
            BsonSerializer.Serialize(bsonWriter, field.FieldType, field.GetValue(value)); 
         
        foreach (var prop in props) 
         
            bsonWriter.WriteName(prop.Name); 
            BsonSerializer.Serialize(bsonWriter, prop.PropertyType, prop.GetValue(value, null)); 
         

         context.Writer.WriteEndDocument(); 
    

然后我注册序列化器:

BsonSerializer.RegisterSerializer(
            typeof(TypedFooBase<TestEvent>),
            new BsonAncientEventSerializer<TestEvent>()
        );

如果我不注册序列化程序,一切正常(除了与旧事件的兼容性)。 但是当我添加注册序列化程序时,代码无法正常工作;

serialize 工作正常(值正确存储在数据库中);

但在阅读时,它不起作用。代码根本不会停在Deserialize

我在代码中访问的集合是IMongoCollection&lt;FooBase&gt;

然后当我执行collection.Find(...).ToListAsync() 时,它给了我一个Cannot create an abstract class 的错误。

这是因为它试图将值转换为FooBase

但它为什么要这么做?为什么,当我不注册序列化程序时可以正常工作?

谢谢。

【问题讨论】:

【参考方案1】:

您看到的行为与您为TypedFooBase&lt;TestEvent&gt; 注册自定义序列化程序有关,但反序列化期间使用的集合类型是FooBase,因此找不到所需的序列化程序。 你可以试试:

将序列化程序映射到基本类型:

  BsonSerializer.RegisterSerializer(
      typeof(FooBase),
      new BsonAncientEventSerializer<TestEvent>()

和适当的序列化程序定义:

public class BsonAncientEventSerializer<T> : SerializerBase<FooBase> where T : class

理论上最好的方法是使用适当的类型来获取结果,例如:

var result = collection.OfType<TypedFooBase<TestEvent>>().Find("").ToList();

但要使其工作,您需要实现在序列化步骤中跳过的discriminator 行为,因为OfType 额外添加了一个未在序列化文档中显示的_t(discriminator) 过滤器。

注意:我没有检查它,但我认为您可以仅为 Find 操作创建一个新的类型化(非基础)集合实例(它本身不会发出任何服务器请求):IMongoCollection&lt;TypedFooBase&lt;TestEvent&gt;&gt;

另外,你可以看看这个doc

更新:听起来像这样:

var result = coll.FindSync("", new FindOptions<FooBase, TypedFooBase<TestEvent>>()).ToList();

应该也可以。

【讨论】:

以上是关于Mongo自定义序列化程序导致无法创建抽象类的主要内容,如果未能解决你的问题,请参考以下文章

将位置坐标的 Mongo BSON 数组反序列化为自定义 C# 类

序列化类更新后不生效

没有@Serializable 的数据类的自定义序列化程序

切换活动而不会丢失自定义数据

第五章自定义序列类

在自定义 Java AWS 应用程序中找不到类的序列化程序