求助:Newtonsoft中不同运行时加载程序集的类JSON类型不兼容
解决多版本程序集并行加载时Newtonsoft.Json反序列化类型冲突问题
针对你遇到的问题——同一服务中加载同名不同版本(甚至同版本但不同加载上下文)的程序集后,Newtonsoft.Json反序列化时因$type字段指向的程序集版本与当前目标类型不匹配导致的冲突,这里提供几个可行的解决思路:
1. 自定义序列化绑定器,忽略程序集版本或指定目标程序集
核心思路是替换Newtonsoft默认的类型解析逻辑,让它在解析$type字段时,忽略版本信息,或者直接从当前要使用的插件程序集中查找类型,而非依赖全局的Type.GetType。
示例代码:
public class PluginTypeBinder : ISerializationBinder { private readonly Assembly _targetAssembly; public PluginTypeBinder(Assembly targetAssembly) { _targetAssembly = targetAssembly; } public Type BindToType(string assemblyName, string typeName) { // 直接从目标插件程序集中查找类型,跳过全局类型解析 return _targetAssembly.GetType(typeName); // 若需保留类型名匹配但忽略版本,可拆分程序集信息后处理 // var assemblyInfo = new AssemblyName(assemblyName); // assemblyInfo.Version = null; // return Type.GetType($"{typeName}, {assemblyInfo}", throwOnError: false); } public void BindToName(Type serializedType, out string assemblyName, out string typeName) { // 序列化时输出不带版本的程序集名称,避免后续反序列化版本冲突 typeName = serializedType.FullName; assemblyName = serializedType.Assembly.GetName().Name; } }
使用方式:
加载插件程序集后,为该插件配置专属的JsonSerializerSettings:
var pluginAssembly = PluginLoader.CreateFromAssemblyFile("path/to/plugin.dll").LoadDefaultAssembly(); var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, SerializationBinder = new PluginTypeBinder(pluginAssembly) }; // 反序列化时使用该专属配置 var obj = JsonConvert.DeserializeObject<YourBaseType>(json, settings);
2. 为每个插件实例独立维护JsonSerializer配置
每个加载的程序集(插件)对应一套独立的JsonSerializerSettings,确保反序列化时始终从当前插件的程序集中解析类型,彻底避免全局类型混淆。该方案需配合上述自定义绑定器使用,是最直接的隔离方案。
3. 禁用TypeNameHandling,改用固定类型反序列化
如果JSON结构固定、无需多态类型解析,可以直接禁用TypeNameHandling,指定明确的目标类型进行反序列化,完全绕过$type字段的解析逻辑:
// 直接指定目标类型,不依赖$type字段 var obj = JsonConvert.DeserializeObject<DerivedClass>(json);
这种方式最简单,但仅适用于不需要多态序列化的场景。
4. 强化程序集加载的上下文隔离
使用McMaster.DotNetCorePlugins的PluginLoadContext为每个版本的程序集创建独立的加载上下文,避免不同版本的程序集类型被全局加载上下文混淆:
// 为v1版本插件创建独立加载上下文 var loadContext1 = new PluginLoadContext("plugin-v1", isCollectible: true); var assembly1 = loadContext1.LoadFromAssemblyFile("path/to/plugin-v1.dll"); // 为v2版本插件创建独立加载上下文 var loadContext2 = new PluginLoadContext("plugin-v2", isCollectible: true); var assembly2 = loadContext2.LoadFromAssemblyFile("path/to/plugin-v2.dll");
配合自定义的PluginTypeBinder,确保每个上下文的类型解析只在自己的程序集中进行,彻底消除类型冲突。
内容的提问来源于stack exchange,提问作者ryryguy
相关产品推荐
相关产品推荐

