你如何“真正”使用Newtonsoft.Json序列化循环引用对象?
我在使用Newtonsoft.Json从我的ASP.NET Web API控制器正确地序列化数据时遇到问题。
这就是我的想法 – 如果我错了,请纠正我。 在某些情况下(特别是当数据中没有任何循环引用时)一切都像您期望的那样工作 – 填充对象列表被序列化并返回。 如果我在模型中引入导致循环引用的数据(如下所述,甚至设置了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 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
或Place
对象的Balances
属性中序列化:
[{"$id":"1","BalanceID":4,"SourceID":2,"PlaceID":2 ...Omitted for clarity...},{"$ref":"4"}]
这个问题是客户端不知道如何处理{$ref:4}
即使我们人类了解正在发生的事情。 就我而言,这意味着我无法使用AngularJS对我的整个余额列表进行ng-repeat
与此JSON,因为它们并非都是具有要绑定的Balance
属性的真实Balance
对象。 我确信还有很多其他用例会出现同样的问题。
我无法关闭json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects
因为很多其他东西都会破坏(这里和其他地方的100个其他问题都有详细记录)。
除了通过Web API控制器中的实体并执行此操作之外,还有更好的解决方法吗?
Balance.Source.Balances = null;
要打破循环引用的所有导航属性? 因为这似乎也不正确。
是的,使用PreserveReferencesHandling.Objects
实际上是使用循环引用序列化对象图的最佳方法,因为它生成最紧凑的JSON并且它实际上保留了对象图的引用结构。 也就是说,当您将JSON反序列化回对象时(使用理解$id
和$ref
表示法的库),对特定对象的每个引用都将指向该对象的同一实例,而不是具有相同的多个实例数据。
在您的情况下,问题是您的客户端解析器不理解Json.Net生成的$id
和$ref
表示法,因此引用未被解析。 这可以通过使用javascript方法在反序列化JSON后重建对象引用来修复。 请参阅此处和此处的示例。
根据您的具体情况,另一种可能的工作方式是在序列化时将ReferenceLoopHandling
设置为Ignore
,而不是将PreserveReferencesHandling
设置为Objects
。 但这不是一个完美的解决方案。 有关使用ReferenceLoopHandling.Ignore
和PreserveReferencesHandling.Objects
之间差异的详细说明,请参阅此问题 。
- 无法添加对’System.Net.Http’的引用。 请确保它在全局程序集缓存中
- 如何使用Ninject InRequestScope处理异步调用?
- 为什么CastleWindsor的BeginScope超出范围?
- 使用ASP Identity 2 POST到/ Token端点时始终收到’invalid_client’错误
- 在OData WebApi Url中传递参数
- 使用Web API和JSON.NET序列化对象时,防止$ id / $ ref
- 在WebAPI中使用ContinueWith进行死锁
- 无法为内容类型multipart配置Web API
- Asp.net WEB API – 如果我使用POST而不是PUT和DELETE会出现什么问题?