Json和循环引用例外

我有一个对象有一个循环引用另一个对象。 鉴于这些对象之间的关系,这是正确的devise。

为了显示

Machine => Customer => Machine 

正如所料,当我尝试使用Json序列化机器或客户对象时遇到了一个问题。 我不确定的是如何解决这个问题,因为我不想破坏机器和客户对象之间的关系。 有什么解决这个问题的select?

编辑

目前我正在使用Controller基类提供的Json方法 。 所以我正在做的序列化和以下一样基本:

 Json(machineForm); 

更新:

不要尝试使用NonSerializedAttribute ,因为JavaScriptSerializer显然忽略它。

而应使用System.Web.Script.SerializationScriptIgnoreAttribute

 public class Machine { public string Customer { get; set; } // Other members // ... } public class Customer { [ScriptIgnore] public Machine Machine { get; set; } // Parent reference? // Other members // ... } 

这样,当你把Machine扔进Json方法时,它将遍历从MachineCustomer的关系,但不会尝试从Customer返回到Machine

您的代码仍然存在这种关系,但JavaScriptSerializer (由Json方法使用)将忽略它。

我回答这个,尽pipe它的年龄,因为它是谷歌“json.encode循环引用”的第三个结果(虽然我不同意上面的答案(完全)),因为使用ScriptIgnoreAttribute假定你将不会在你的代码中的任何地方想要遍历一些JSON的另一个方向的关系。 我不相信因为一个用例而locking你的模型。

它激发了我使用这个简单的解决scheme。

由于你在MVC中使用View,所以你有了Model,你只需要简单地将Model分配给你的控制器中的ViewData.Model,然后在你的View中使用LINQ查询来很好地平滑数据,去掉违规你想要这样的特定JSON的循环引用:

 var jsonMachines = from m in machineForm select new { mX, mY, // other Machine properties you desire Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire }}; return Json(jsonMachines); 

或者,如果机器 – >客户关系是1 .. * – > *然后尝试:

 var jsonMachines = from m in machineForm select new { mX, mY, // other machine properties you desire Customers = new List<Customer>( (from c in m.Customers select new Customer() { Id = c.Id, Name = c.Name, // Other Customer properties you desire }).Cast<Customer>()) }; return Json(jsonMachines); 

基于txl的答案,你必须禁用延迟加载和代理创build,你可以使用正常的方法来获取你的数据。

例:

 //Retrieve Items with Json: public JsonResult Search(string id = "") { db.Configuration.LazyLoadingEnabled = false; db.Configuration.ProxyCreationEnabled = false; var res = db.Table.Where(a => a.Name.Contains(id)).Take(8); return Json(res, JsonRequestBehavior.AllowGet); } 

用来有相同的问题。 我创build了一个简单的扩展方法,将L2E对象“扁平化”为一个IDictionary。 一个IDictionary被JavaScriptSerializer正确序列化。 生成的Json与直接序列化对象相同。

由于我限制了序列化的级别,所以避免了循环引用。 它也不包括1-> n链接表(Entitysets)。

  private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) { var result = new Dictionary<string, object>(); var myType = data.GetType(); var myAssembly = myType.Assembly; var props = myType.GetProperties(); foreach (var prop in props) { // Remove EntityKey etc. if (prop.Name.StartsWith("Entity")) { continue; } if (prop.Name.EndsWith("Reference")) { continue; } // Do not include lookups to linked tables Type typeOfProp = prop.PropertyType; if (typeOfProp.Name.StartsWith("EntityCollection")) { continue; } // If the type is from my assembly == custom type // include it, but flattened if (typeOfProp.Assembly == myAssembly) { if (currLevel < maxLevel) { result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1)); } } else { result.Add(prop.Name, prop.GetValue(data, null)); } } return result; } public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) { return JsonFlatten(data, maxLevel, 1); } 

我的操作方法如下所示:

  public JsonResult AsJson(int id) { var data = Find(id); var result = this.JsonFlatten(data); return Json(result, JsonRequestBehavior.AllowGet); } 

在entity framework版本4中 ,有一个选项可用:ObjectContextOptions.LazyLoadingEnabled

设置为false应该避免“循环引用”问题。 但是,您将不得不显式加载要包含的导航属性。

请参阅: http : //msdn.microsoft.com/en-us/library/bb896272.aspx

因为,据我所知,你不能序列化对象引用,但只有副本,你可以尝试采用一些肮脏的黑客,如下所示:

  1. 客户应将其机器参考序列化为机器的标识
  2. 当你反序列化json代码时,你可以在它上面运行一个简单的函数,将这些id转换成适当的引用。

你需要决定哪个是“根”对象。 说机器是根,那么客户就是机器的一个子对象。 在序列化机器时,它会将客户序列化为JSON中的子对象,当客户序列化时,它不会将其序列化为对机器的引用。 当你的代码反序列化机器时,它将对机器的客户子对象进行反序列化,并将客户的反向引用恢复到机器上。

大多数序列化库提供了一些钩子来修改为每个类执行反序列化的方式。 您需要使用该挂钩来修改机器类的反序列化,以便恢复机器客户中的反向引用。 究竟是什么钩子取决于你正在使用的JSON库。

本周我也遇到了同样的问题,并且不能使用匿名types,因为我需要实现一个请求List<MyType>的接口。 做出一个图表显示与导航性的所有关系后,我发现MyTypeMyObject有一个双向的关系,它引起了这个循环引用,因为它们都互相保存。

在确定MyObject并不需要知道MyType ,从而使它成为单向关系之后,这个问题就解决了。