.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
相关产品推荐
相关产品推荐

