You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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来重写类型查找规则:

  1. 实现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;
        }
    }
    
  2. 在反序列化时启用这个自定义绑定器:
    var jsonSettings = new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto, // 要和原来序列化时的配置一致
        SerializationBinder = new DtoSerializationBinder()
    };
    var deserializedObj = JsonConvert.DeserializeObject<YourTargetType>(jsonString, jsonSettings);
    

内容的提问来源于stack exchange,提问作者Eterm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 23:17:44