如何在BSON反序列化时忽略已标记Obsolete或已删除的类
解决删除过时派生类后的BSON反序列化问题
你遇到的核心问题是MongoDB的BSON序列化依赖类型鉴别器(默认是_t字段),删除Fetus类后,反序列化时找不到对应类型就会报错。下面是能实现「先删代码再清数据库」的具体方案:
临时类型映射兜底
先通过自定义BSON映射,把数据库里原Fetus类型的记录映射到现有类型或者临时替代类,保证反序列化不报错。
1. 自定义鉴别器处理
在应用启动时配置BSON类映射,当遇到_t: "Fetus"的记录时,自动转成现有Son/Daughter或者临时空实现:
// 启动时注册BSON映射 BsonClassMap.RegisterClassMap<IChild>(cm => { cm.AutoMap(); cm.SetDiscriminatorIsRequired(true); cm.AddKnownType(typeof(Son)); cm.AddKnownType(typeof(Daughter)); // 替换默认鉴别器为自定义逻辑 cm.SetDiscriminatorConvention(new FetusDiscriminatorFix()); }); public class FetusDiscriminatorFix : IDiscriminatorConvention { public string ElementName => "_t"; public Type GetActualType(BsonReader bsonReader, Type nominalType) { var bookmark = bsonReader.GetBookmark(); bsonReader.ReadStartDocument(); string discriminator = null; if (bsonReader.FindElement(ElementName)) { discriminator = bsonReader.ReadString(); } bsonReader.ReturnToBookmark(bookmark); // 把旧的Fetus类型映射到Son,或者下面的EmptyChild if (discriminator == "Fetus") { return typeof(Son); } // 其他类型正常处理 return discriminator switch { "Son" => typeof(Son), "Daughter" => typeof(Daughter), _ => nominalType }; } public BsonValue GetDiscriminator(Type nominalType, Type actualType) { return actualType.Name; } } // 要是不想映射到现有类型,也可以整个临时空实现 public class EmptyChild : IChild { public string Name { get; set; } public string Gender => "Unknown"; }
2. 忽略未知类型(快速兜底)
如果完全不需要保留Fetus的数据,可以直接配置反序列化时跳过无法识别的类型:
// 注册自定义序列化器,遇到未知类型直接返回默认值 BsonSerializer.RegisterSerializer<IChild>(new IgnoreUnknownChildSerializer()); public class IgnoreUnknownChildSerializer : SerializerBase<IChild> { public override IChild Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var reader = context.Reader; var bookmark = reader.GetBookmark(); try { return BsonSerializer.Deserialize<IChild>(reader); } catch (BsonSerializationException) { // 反序列化失败就跳过这个元素 reader.ReturnToBookmark(bookmark); reader.SkipValue(); return null; } } }
分步清理流程
- 先上兜底版本:把带上述映射的代码部署上线,确保应用能正常读取数据库里的旧数据,同时可以在业务逻辑里把
Fetus记录标记为待清理,或者直接转换为其他类型。 - 批量清理数据库:等应用稳定后,用MongoDB脚本批量处理旧数据:
// 方案1:直接移除所有Fetus类型的子元素 db.Parent.updateMany({}, { $pull: { "Children": { "_t": "Fetus" } } }) // 方案2:把Fetus转成Son类型 db.Parent.updateMany( { "Children._t": "Fetus" }, { $set: { "Children.$._t": "Son", "Children.$.Gender": "Male" } } ) - 移除临时代码:等数据库里所有
Fetus记录都处理完,就可以删掉之前加的映射和临时类,彻底完成清理。
这样就能按你想要的顺序,先删Fetus类代码,再慢慢清理数据库,不会触发反序列化错误。
内容的提问来源于stack exchange,提问作者APW
相关产品推荐
相关产品推荐

