Newtonsoft.Json反序列化System.Type抛出JsonSerializationException求助
问题分析与解决方案
核心问题
异常本质是反序列化时无法根据JSON中带版本号的程序集限定名找到对应System.Type,仅在新环境出现的原因是:老环境存在程序集绑定重定向、本地旧版本DLL缓存或GAC安装记录,而新环境缺少这些配置/文件,导致Type.GetType()无法定位到目标类型。
解决方案
1. 自定义Type转换器,忽略版本号加载类型
在反序列化时手动剥离版本信息,从当前加载的程序集中匹配类型:
// 自定义转换器:忽略版本号解析Type public class TypeIgnoreVersionConverter : JsonConverter<Type> { public override Type ReadJson(JsonReader reader, Type objectType, Type existingValue, bool hasExistingValue, JsonSerializer serializer) { var fullTypeName = reader.Value as string; if (string.IsNullOrEmpty(fullTypeName)) return null; // 拆分类型名与程序集信息,剔除版本、公钥等冗余部分 var typeAssemblyParts = fullTypeName.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); if (typeAssemblyParts.Length < 2) return Type.GetType(fullTypeName); var typeName = typeAssemblyParts[0]; var assemblyName = typeAssemblyParts[1].Split(new[] { ", Version=" }, StringSplitOptions.RemoveEmptyEntries)[0]; // 从当前AppDomain加载的程序集中查找目标类型 var targetAssembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.OrdinalIgnoreCase)); return targetAssembly?.GetType(typeName, true); } public override void WriteJson(JsonWriter writer, Type value, JsonSerializer serializer) { writer.WriteValue(value.AssemblyQualifiedName); } }
修改CloneJson方法的序列化配置,添加该转换器:
public static T CloneJson<T>(this T source) { if ((object)source == null) return default(T); var settings = new Newtonsoft.Json.JsonSerializerSettings() { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace, TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto, Converters = new List<JsonConverter> { new TypeIgnoreVersionConverter() } // 添加自定义转换器 }; var serializedObj = Newtonsoft.Json.JsonConvert.SerializeObject((object)source, settings); var deserializedOjb = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(serializedObj, settings); return deserializedOjb; }
2. 添加程序集绑定重定向
在项目的App.config中配置绑定重定向,强制所有版本的目标程序集指向当前环境的版本:
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="CompanyName.ApplicationName.Common.Objects" publicKeyToken="null" culture="neutral" /> <!-- 替换newVersion为你当前环境的程序集版本号 --> <bindingRedirect oldVersion="0.0.0.0-99.99.99.99" newVersion="1.0.8977.16740" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
3. 调整序列化配置,避免存储完整版本号
如果不需要跨程序集的类型识别,可以修改TypeNameHandling并自定义序列化逻辑,只存储类型全名和程序集名称:
public class TypeSimpleNameConverter : JsonConverter<Type> { public override Type ReadJson(JsonReader reader, Type objectType, Type existingValue, bool hasExistingValue, JsonSerializer serializer) { var typeInfo = reader.Value as string; return Type.GetType(typeInfo, true); } public override void WriteJson(JsonWriter writer, Type value, JsonSerializer serializer) { // 只输出类型全名+程序集名称,不带版本、公钥信息 writer.WriteValue($"{value.FullName}, {value.Assembly.GetName().Name}"); } }
然后在CloneJson的settings中替换TypeNameHandling为None并添加该转换器。
排查验证步骤
- 检查新环境输出目录中
CompanyName.ApplicationName.Common.Objects.dll的版本,对比JSON中的版本号是否一致 - 复制团队成员的
App.config配置到本地,确认是否存在绑定重定向 - 用ILSpy查看编译输出的程序集版本,确认项目引用的版本与实际输出一致
- 检查本地GAC是否安装了旧版本的目标程序集,如有则卸载
内容的提问来源于stack exchange,提问作者Jacob Thomas
相关产品推荐
相关产品推荐

