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

.NET 6中JSON序列化循环引用异常问题及解决方案咨询

解决.NET 6 JSON序列化循环引用/深度超限问题

先排查配置不生效的核心原因

你之前添加的控制器配置仅对控制器Action返回结果的序列化生效,如果是你手动调用JsonSerializer.Serialize或JsonConvert.SerializeObject做序列化,这些全局配置不会起作用,需要单独处理;另外,同时混用AddNewtonsoftJson和AddJsonOptions会导致配置冲突,因为它们对应两套不同的序列化器(Newtonsoft.Json和System.Text.Json),这也是配置失效的常见原因。

针对不同场景的具体解决方案

1. 控制器返回对象场景(配置生效范围)

确保Action直接返回对象/IActionResult,而非手动序列化后返回字符串。清空冲突配置,单独启用一套序列化器:

// 切换到Newtonsoft.Json(处理循环引用更成熟)
builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        // 可选:强制限制序列化深度为1
        options.SerializerSettings.MaxDepth = 1;
    });

或者单独使用System.Text.Json的配置:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        options.JsonSerializerOptions.MaxDepth = 1;
    });

2. 手动调用序列化方法场景

如果是自己写代码做序列化,需要在调用时传入对应配置:

  • System.Text.Json手动序列化:
    var serializeOptions = new JsonSerializerOptions
    {
        ReferenceHandler = ReferenceHandler.IgnoreCycles,
        MaxDepth = 1
    };
    string json = JsonSerializer.Serialize(yourDataList, serializeOptions);
    
  • Newtonsoft.Json手动序列化:
    var serializeSettings = new Newtonsoft.Json.JsonSerializerSettings
    {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
        MaxDepth = 1
    };
    string json = Newtonsoft.Json.JsonConvert.SerializeObject(yourDataList, serializeSettings);
    

3. 强制忽略所有导航属性(仅序列化第一层)

如果只想保留实体的基础属性,完全排除导航属性,有两种方式:

  • 特性标记(快速但不灵活):在导航属性上直接加[JsonIgnore](对应System.Text.Json)或[Newtonsoft.Json.JsonIgnore](对应Newtonsoft.Json),但需要修改实体类。
  • 自定义序列化过滤(灵活无侵入):用Newtonsoft.Json的ContractResolver实现动态过滤,只保留值类型、字符串等基础属性:
    public class FirstLevelPropertyResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
    {
        protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
        {
            var allProperties = base.CreateProperties(type, memberSerialization);
            // 只保留值类型、字符串、可空值类型的属性,过滤引用类型导航属性
            return allProperties.Where(p => 
                p.PropertyType.IsValueType || 
                p.PropertyType == typeof(string) ||
                (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            ).ToList();
        }
    }
    
    // 使用示例
    var settings = new Newtonsoft.Json.JsonSerializerSettings
    {
        ContractResolver = new FirstLevelPropertyResolver(),
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    };
    string json = Newtonsoft.Json.JsonConvert.SerializeObject(yourDataList, settings);
    

关于DTO是否是最终解决方案

DTO确实是最规范、最可控的长期解决方案,尤其是在中大型项目中:

  • 可以精准定义要序列化的字段,避免暴露敏感或不必要的属性
  • 从根源上杜绝循环引用,因为DTO不会包含实体间的反向导航属性
  • 后续需求变更时,修改DTO不会影响核心实体类的设计

如果是临时解决小问题,用上述配置或过滤方法即可;如果项目需要长期维护,建议优先落地DTO方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:21:37