如何将DTO迁移至依赖程序集且不破坏现有数据反序列化?
DTO拆分后的Newtonsoft.Json反序列化兼容方案
问题背景
我有一个包含两个模块的.NET Framework项目(简化示例结构):
CompanyName.ProjectName /consumers /DTOs
希望将/DTOs拆分到独立项目中,调整后结构为:
CompanyName.ProjectName /consumers CompanyName.ProjectName.Dtos /DTOs
目的是把DTO转为.NET Standard 2.0,供.NET 8项目调用。由于Consumers模块依赖Framework,暂无法转换整个项目。迁移DTO后构建可正常运行,但历史序列化JSON数据中包含类型信息:
{ "$type": "CompanyName.Project.Namespace.Item, CompanyName.ProjectName", ... }
迁移后即使保留命名空间,反序列化仍失败,因为无法在CompanyName.ProjectName程序集中找到NameSpace.Item。需要解决两个方向的问题:能否通过编译配置让类型仍可在原项目程序集中被找到?或者能否修改NewtonSoft.Json的类型处理逻辑,使其也在新程序集中查找类型?
解决方案
方案一:通过类型转发实现编译层面兼容
可以在原CompanyName.ProjectName项目中添加对新CompanyName.ProjectName.Dtos项目的引用,然后用类型转发特性让CLR自动将原程序集的类型请求导向新程序集:
- 在原项目中创建一个类文件(比如
TypeForwarders.cs),写入以下代码:using System.Runtime.CompilerServices; using CompanyName.Project.Namespace; // 新DTO项目的命名空间,要和原DTO完全一致 [assembly: TypeForwardedTo(typeof(Item))] // 如果有多个DTO,每个类型都加一行TypeForwardedTo [assembly: TypeForwardedTo(typeof(AnotherItem))] - 原理:这个特性会告诉CLR,当代码或序列化框架尝试从
CompanyName.ProjectName程序集加载Item类型时,直接去CompanyName.ProjectName.Dtos程序集查找。无需修改任何反序列化逻辑,历史JSON数据可以直接正常解析。 - 注意:必须保证新DTO项目的类名、命名空间和原DTO完全一致,原项目要引用新的DTO项目。
方案二:自定义Newtonsoft.Json的类型绑定逻辑
如果不想修改原项目的编译配置,可以自定义ISerializationBinder来重写类型查找规则:
- 实现
ISerializationBinder接口,在类型查找时兼容新旧程序集:public class DtoSerializationBinder : ISerializationBinder { public Type BindToType(string assemblyName, string typeName) { // 当遇到原程序集的类型请求时,优先尝试新程序集 if (assemblyName.Equals("CompanyName.ProjectName", StringComparison.OrdinalIgnoreCase)) { var newType = Type.GetType($"{typeName}, CompanyName.ProjectName.Dtos"); if (newType != null) return newType; } // 其他情况按默认逻辑加载类型 return Type.GetType($"{typeName}, {assemblyName}"); } public void BindToName(Type serializedType, out string assemblyName, out string typeName) { // 序列化时可以选择输出原程序集名称(保持历史兼容性),或者输出新程序集名称 // 如果要兼容未来的新数据,建议输出新程序集名称 assemblyName = serializedType.Assembly.GetName().Name; typeName = serializedType.FullName; } } - 在反序列化时启用这个自定义绑定器:
var jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, // 要和原来序列化时的配置一致 SerializationBinder = new DtoSerializationBinder() }; var deserializedObj = JsonConvert.DeserializeObject<YourTargetType>(jsonString, jsonSettings);
内容的提问来源于stack exchange,提问作者Eterm
相关产品推荐
相关产品推荐

