我在使用 Newtonsoft.Json 从我的 ASP.NET Web API Controller 正确序列化某些数据时遇到问题。
这就是我认为 正在发生的事情 - 如果我错了请纠正我。在某些情况下(特别是当数据中没有任何循环引用时)一切都像您期望的那样工作 - 填充对象列表被序列化并返回。如果我在模型中引入导致循环引用的数据(如下所述,甚至设置了 PreserveReferencesHandling.Objects
),则只有指向具有循环引用的第一个对象的列表中的元素才会序列化客户可以“使用”的一种方式。如果在将内容发送到序列化程序之前排序不同,则“导致的元素”可以是数据中的任何元素,但至少有一个将以客户端可以“使用”的方式序列化。空对象最终被序列化为 Newtonsoft 引用 ({$ref:X}
)。
例如,如果我有一个带有导航属性的 EF 模型,如下所示:
在我的 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
实体具有相同的 Source
或 Place
,序列化就会变成后面的 Balance
我返回到 Newtonsoft 引用的最顶层列表的对象而不是它们的完整对象,因为它们已经在 Source
的 Balances
属性中序列化或 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 方法重建对象引用来解决。参见 here和 here举些例子。
根据您的情况,另一种可行的方法是在序列化时将 ReferenceLoopHandling
设置为 Ignore
而不是将 PreserveReferencesHandling
设置为 对象
。但这不是一个完美的解决方案。参见 this question有关使用 ReferenceLoopHandling.Ignore
和 PreserveReferencesHandling.Objects
之间差异的详细说明。
关于c# - 如何使用 Newtonsoft.Json 序列化 "Really"循环引用对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26434738/