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

.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:

  1. 配置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属性或手动复制实现。
  1. 注册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相关注解的解析:

  1. 实现自定义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);
}
  1. 序列化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:

  1. 创建域配置文件
    分别为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>
  1. 在独立域中执行逻辑
// 处理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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:07:03