我在使用Newtonsoft.Json从ASP.NET Web API控制器中正确序列化一些数据时遇到了问题。
下面是我的情况 认为 是怎么回事--如果我错了,请纠正我。 在某些情况下(特别是当数据中没有任何循环引用时),一切都像你所期望的那样工作--一个填充对象的列表被序列化并返回。 如果我引入的数据在模型中导致循环引用(如下所述,即使是用 PreserveReferencesHandling.Objects
set),只有列表中第一个循环引用对象之前的元素才会以客户端可以 "使用 "的方式被序列化。 前面的元素 "可以是数据中的任何一个元素,如果在发送东西到序列化器之前,它的排序方式不同的话,但至少有一个元素会以客户端可以 "使用 "的方式序列化。 空对象最终会被序列化为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的引用中,而不是它们的完整对象,因为它们已经在 Balances
的财产 Source
或 Place
对象。
[{"$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反序列化回对象时(使用一个能理解JSON的库)。$id
和 $ref
符号),每个对特定对象的引用都将指向该对象的同一个实例,而不是拥有多个具有相同数据的实例。
在您的案例中,问题在于您的客户端解析器不理解 $id
和 $ref
Json.Net产生的符号,所以引用没有被解析。 这可以通过使用javascript方法在反序列化JSON后重建对象引用来解决。请看 此处 和 此处 的例子。
另一种可能的做法是根据你的情况,在你的电脑上设置 ReferenceLoopHandling
到 Ignore
而不是在序列化时设置 PreserveReferencesHandling
到 Objects
. 这虽然不是一个完美的解决方案。请看 这个问题 详细解释使用 ReferenceLoopHandling.Ignore
和 PreserveReferencesHandling.Objects
.
我写了一个最小的程序来测试这个。这是我的github。https:/github.comassafwo1TestSerializeJsonObjects。. 这是代码。
using Newtonsoft.Json;
using System.Diagnostics;
namespace TestSerializeJsonObjects
{
class Program
{
public class Node
{
public Node Next { get; set; }
}
static void Main(string[] args)
{
// create new node
var head = new Node();
// point its "next" field at itself
head.Next = head;
// this is now the smallest circular reference data structure possible
// assert that head next is head
Debug.Assert(head.Next == head);
// save to string
var s = JsonConvert.SerializeObject(head, new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
// get from string
var head2 = JsonConvert.DeserializeObject<Node>(s, new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
// assert that head2 next is head2
Debug.Assert(head2.Next == head2);
// done
}
}
}