.NET Framework中不同版本System.Text.Json序列化异常解决问询
解决.NET Framework 4.6.2中System.Text.Json多版本兼容与序列化异常问题
针对你遇到的项目Z同时依赖第三方X(System.Text.Json 7.0.0)、Y(System.Text.Json 6.0.1),且自身使用Newtonsoft.Json序列化的版本冲突问题,以下是三种可落地的解决方案:
方案一:程序集绑定重定向+自定义加载解析
通过配置代码基和注册程序集解析事件,让CLR能根据需求加载对应版本的System.Text.Json:
- 配置app.config代码基
在Z项目的app.config中添加两个版本的程序集路径映射,确保CLR知道去哪里找对应版本的DLL:
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <!-- 映射System.Text.Json 6.0.1 --> <dependentAssembly> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <codeBase version="6.0.1.0" href="lib\System.Text.Json.6.0.1\System.Text.Json.dll" /> </dependentAssembly> <!-- 映射System.Text.Json 7.0.0 --> <dependentAssembly> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <codeBase version="7.0.0.0" href="lib\System.Text.Json.7.0.0\System.Text.Json.dll" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
- 需将两个版本的
System.Text.Json.dll分别放在项目输出目录的lib\System.Text.Json.6.0.1和lib\System.Text.Json.7.0.0子文件夹中,可通过NuGet包的Copy Local属性或手动复制实现。
- 注册AssemblyResolve事件
在Z项目的启动入口(如Program.cs或Global.asax)添加自定义解析逻辑,确保CLR加载正确版本:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); if (assemblyName.Name != "System.Text.Json") return null; var version = assemblyName.Version.ToString(); var assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"lib\\System.Text.Json.{version}\\System.Text.Json.dll"); return File.Exists(assemblyPath) ? Assembly.LoadFrom(assemblyPath) : null; };
此逻辑会在CLR找不到对应版本的程序集时,手动加载指定路径下的目标版本。
方案二:自定义Newtonsoft契约解析器跳过STJ注解
如果异常仅发生在序列化Y的对象时(因反射到STJ6的注解触发版本加载),可通过自定义契约解析器跳过System.Text.Json相关注解的解析:
- 实现自定义ContractResolver
public class SkipStjAnnotationsResolver : Newtonsoft.Json.Serialization.DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); // 过滤所有System.Text.Json的注解 property.AttributeProvider = new FilteredAttributeProvider(member, typeof(System.Text.Json.Serialization.JsonAttribute)); return property; } } public class FilteredAttributeProvider : IAttributeProvider { private readonly MemberInfo _member; private readonly Type _excludeType; public FilteredAttributeProvider(MemberInfo member, Type excludeType) { _member = member; _excludeType = excludeType; } public IEnumerable<Attribute> GetAttributes(bool inherit) => _member.GetCustomAttributes(inherit).Where(a => !_excludeType.IsAssignableFrom(a.GetType())); public IEnumerable<Attribute> GetAttributes(Type attributeType, bool inherit) => _excludeType.IsAssignableFrom(attributeType) ? Enumerable.Empty<Attribute>() : _member.GetCustomAttributes(attributeType, inherit); public bool IsDefined(Type attributeType, bool inherit) => !_excludeType.IsAssignableFrom(attributeType) && _member.IsDefined(attributeType, inherit); }
- 序列化Y对象时使用自定义解析器
var settings = new Newtonsoft.Json.JsonSerializerSettings { ContractResolver = new SkipStjAnnotationsResolver() }; var json = Newtonsoft.Json.JsonConvert.SerializeObject(yObject, settings);
此方法避免Newtonsoft解析Y对象中的STJ注解,从而不会触发STJ6版本的加载请求。
方案三:应用程序域隔离(适用于逻辑可拆分场景)
如果X和Y的业务逻辑可完全独立执行,可通过创建独立应用程序域隔离不同版本的STJ:
- 创建域配置文件
分别为X和Y创建独立的配置文件:
x-domain.config(绑定STJ7):
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" /> <codeBase version="7.0.0.0" href="lib\System.Text.Json.7.0.0\System.Text.Json.dll" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
y-domain.config(绑定STJ6):
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" /> <codeBase version="6.0.1.0" href="lib\System.Text.Json.6.0.1\System.Text.Json.dll" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
- 在独立域中执行逻辑
// 处理X的逻辑(使用STJ7) var xDomainSetup = new AppDomainSetup { ConfigurationFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "x-domain.config") }; var xDomain = AppDomain.CreateDomain("XDomain", null, xDomainSetup); xDomain.DoCallBack(() => { var xService = new X.SomeService(); xService.Execute(); }); AppDomain.Unload(xDomain); // 处理Y的逻辑(使用STJ6) var yDomainSetup = new AppDomainSetup { ConfigurationFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "y-domain.config") }; var yDomain = AppDomain.CreateDomain("YDomain", null, yDomainSetup); yDomain.DoCallBack(() => { var yData = new Y.SomeData(); var json = Newtonsoft.Json.JsonConvert.SerializeObject(yData); }); AppDomain.Unload(yDomain);
注意:跨域传递的对象需实现ISerializable或继承MarshalByRefObject,否则会触发对象拷贝导致版本冲突。
内容的提问来源于stack exchange,提问作者V0ldek
相关产品推荐
相关产品推荐

