Newtonsoft.Json.JsonSerializationException
前言:
1 本文中内容全部手敲,如果存在错误,请在评论区指正
2 本文作学习探索使用,不涉及任何商业用途
3 本文会随着作者在软件开发过程中遇到问题,解决问题后不定期更新
一 自引用循环错误(self referencing loop)
1.1 错误原因
对象之间存在循环引用(A 引用 B,B 又引用 A)
1.2 常见场景
1.2.1 异步任务状态机(AsyncTaskMethodBuilder
)
① 如何理解
异步任务状态机是比较官方说法,你可以理解为异步任务(Task)返回的对象未完成就被调用序列化方法了。
② 真实原理
一些编程语言,例如C# 会在编译(生成解决方案或者程序集的阶段)的时候生成一个状态机类(继承自IAsyncStateMachine),比如你的异步方法名称叫做 CreateFHDInput, 那么就会可能生成一个<CreateFHDInput>d__13 的类。其中d标识状态机类,13标识一个自增后缀,用于区分同名的多个生成类。 这个<CreateFHDInput>d__13 中有三个属性。
I 当前的执行状态(_state)
II 任务构造器(<>t_builder 类型为AsyncTaskMethodBuilder<T>)
III 局部变量和上下文(_locals)
上面的变量之间相互引用,所以当你序列化一个未完成的任务返回对象时就会导致自引用循环错误
③ 错误复现
下面的代码中CreateFHDInput是个异步方法 并返回一个对象
foreach (var item in input)
{ywy=await FindYWY(data1, item.YWY);var content = CreateFHDInput(item, isCancel);string jsonContent = JsonConvert.SerializeObject(content);var res = await Request(jsonContent, Tools.RequestMethods.POST);await InsertAPIRecord(res, db, item.FHDH+item.FHHH.ToString("D3"), isCancel,Consts.LX_S);
}
在这个示例中,异步方法并没有使用await等待 任务执行完成,断点调试结果如下
因为未完成此任务,content对象仍然是一个状态机类的对象,状态是waitingforActivation等待任务结束。这是调用序列化方法就会导致循环引用报错。
④ 正确示例
任务前加上await
foreach (var item in input)
{ywy=await FindYWY(data1, item.YWY);var content = await CreateFHDInput(item, isCancel);string jsonContent = JsonConvert.SerializeObject(content);var res = await Request(jsonContent, Tools.RequestMethods.POST);await InsertAPIRecord(res, db, item.FHDH+item.FHHH.ToString("D3"), isCancel,Consts.LX_S);
}
⑤: 解决方案
I 使用await等待任务结束
II 配置序列化设置忽略循环引用 (此方式我未验证)
// Newtonsoft.Json 配置
var settings = new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore // 忽略循环引用
};
string json = JsonConvert.SerializeObject(yourObject, settings);// System.Text.Json 配置
var options = new JsonSerializerOptions {ReferenceHandler = ReferenceHandler.IgnoreCycles // .NET 6+ 支持
};
string json = JsonSerializer.Serialize(yourObject, options);