c# - 如何使用 Newtonsoft.Json 序列化 "Really"循环引用对象?

标签 c# json entity-framework asp.net-web-api json.net

我在使用 Newtonsoft.Json 从我的 ASP.NET Web API Controller 正确序列化某些数据时遇到问题。

这就是我认为 正在发生的事情 - 如果我错了请纠正我。在某些情况下(特别是当数据中没有任何循环引用时)一切都像您期望的那样工作 - 填充对象列表被序列化并返回。如果我在模型中引入导致循环引用的数据(如下所述,甚至设置了 PreserveReferencesHandling.Objects),则只有指向具有循环引用的第一个对象的列表中的元素才会序列化客户可以“使用”的一种方式。如果在将内容发送到序列化程序之前排序不同,则“导致的元素”可以是数据中的任何元素,但至少有一个将以客户端可以“使用”的方式序列化。空对象最终被序列化为 Newtonsoft 引用 ({$ref:X})。

例如,如果我有一个带有导航属性的 EF 模型,如下所示:

Model

在我的 global.asax 中:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;

这是我使用 Entity Framework 执行的基本查询(延迟加载已关闭,因此此处没有任何代理类):

[HttpGet]
[Route("starting")]
public IEnumerable<Balance> GetStartingBalances()
{
   using (MyContext db = new MyContext())
   {
       var data = db.Balances
        .Include(x => x.Source)
        .Include(x => x.Place)
        .ToList()
       return data;
    }
}

到目前为止一切顺利,data 已填充。

如果没有循环引用,生活就是美好的。但是,一旦有 2 个 Balance 实体具有相同的 SourcePlace,序列化就会变成后面的 Balance 我返回到 Newtonsoft 引用的最顶层列表的对象而不是它们的完整对象,因为它们已经在 SourceBalances 属性中序列化或 Place 对象:

[{"$id":"1","BalanceID":4,"SourceID":2,"PlaceID":2 ...Omitted for clarity...},{"$ref":"4"}]

这个问题是客户端不知道如何处理 {$ref:4},即使我们人类明白发生了什么。就我而言,这意味着我不能使用 AngularJS 通过此 JSON 在我的整个余额列表上 ng-repeat,因为它们并非都是真正的 Balance 对象Balance 属性进行绑定(bind)。我敢肯定还有很多其他用例会遇到同样的问题。

我无法关闭 json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects 因为很多其他的事情会中断(这在 100 个其他问题中有详细记录在这里和其他地方).

除了遍历 Web API Controller 中的实体并执行此操作之外,是否有更好的解决方法

Balance.Source.Balances = null;

所有的导航属性来打破循环引用?因为这似乎也不对。

最佳答案

是的,使用 PreserveReferencesHandling.Objects 确实是使用循环引用序列化对象图的最佳方式,因为它生成最紧凑的 JSON,并且实际上保留了对象图的引用结构。也就是说,当您将 JSON 反序列化回对象时(使用理解 $id$ref 符号的库),对特定对象的每个引用都将指向该对象的相同实例,而不是具有具有相同数据的多个实例。

在你的情况下,问题是你的客户端解析器不理解 Json.Net 生成的 $id$ref 符号,所以引用没有被解决。这可以通过在反序列化 JSON 后使用 javascript 方法重建对象引用来解决。参见 herehere举些例子。

根据您的情况,另一种可行的方法是在序列化时将 ReferenceLoopHandling 设置为 Ignore 而不是将 PreserveReferencesHandling 设置为 对象。但这不是一个完美的解决方案。参见 this question有关使用 ReferenceLoopHandling.IgnorePreserveReferencesHandling.Objects 之间差异的详细说明。

关于c# - 如何使用 Newtonsoft.Json 序列化 "Really"循环引用对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26434738/

相关文章:

c# - 执行 SaveChanges 时排除未修改的字段

asp.net - Entity Framework 上下文为静态

c# - 在 UI 线程上创建和启动任务

json - iOS 14 中的通用链接 apple-app-site-association 文件

javascript - 是否有任何简写方法将以标题作为第一个数组的字符串数组转换为数组对象?

php - 使用 JSON/PHP 将 DATETIME 插入 mysql 数据库

c# - "Column names in each table must be unique."数据库更新期间

c# - 使用 MEF 导入多个实例

c# - 如何从 Asp Net Core WebApi 将日志存储到 Azure 表存储?

c# - 从流中读取文件