Json and Circular Reference Exception
我有一个对象,它对另一个对象有循环引用。考虑到这些对象之间的关系,这是正确的设计。
举例说明
1 | Machine => Customer => Machine |
正如预期的那样,当我试图使用JSON序列化机器或客户对象时,会遇到一个问题。我不确定的是如何解决这个问题,因为我不想破坏机器和客户对象之间的关系。解决此问题的选项有哪些?
编辑
目前我正在使用控制器基类提供的JSON方法。因此,我正在进行的序列化基本如下:
1 | Json(machineForm); |
号
更新:
不要试图使用
相反,在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Machine { public string Customer { get; set; } // Other members // ... } public class Customer { [ScriptIgnore] public Machine Machine { get; set; } // Parent reference? // Other members // ... } |
这样,当你把一个
您的代码仍然可以按照自己喜欢的方式进行处理,但是
我之所以回答这个问题,是因为它是谷歌(Google)针对"json.encode循环引用"的第三个结果(目前),尽管我不完全同意上面的答案(完全同意),因为使用scriptIgnoreAttribute会假定您的代码中没有任何地方想要在其他方向上遍历某些json的关系。我不相信因为一个用例而锁定你的模型。
这确实激励了我使用这个简单的解决方案。
因为您在MVC中的视图中工作,所以您有了模型,只需将模型分配给view data.model在控制器中,继续使用视图中的LINQ查询来很好地平展数据,删除您想要的特定JSON的有问题的循环引用,如下所示:
1 2 3 4 5 | var jsonMachines = from m in machineForm select new { m.X, m.Y, // other Machine properties you desire Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire }}; return Json(jsonMachines); |
。
或者,如果机器->客户关系为1….->*则尝试:
1 2 3 4 5 6 7 8 9 10 11 12 | var jsonMachines = from m in machineForm select new { m.X, m.Y, // 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的答案,你必须禁用延迟加载和代理创建,您可以使用常规方法获取数据。
例子:
1 2 3 4 5 6 7 8 9 10 | //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); } |
。
用于有相同的问题。我创建了一个简单的扩展方法,它将L2e对象"展平"为IDictionary。IDictionary由JavaScriptSerializer正确序列化。生成的JSON与直接序列化对象的JSON相同。
由于我限制了序列化的级别,因此避免了循环引用。它也不包括1->n个链接表(EntitySets)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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); } |
号
我的操作方法如下:
1 2 3 4 5 | public JsonResult AsJson(int id) { var data = Find(id); var result = this.JsonFlatten(data); return Json(result, JsonRequestBehavior.AllowGet); } |
在Entity Framework版本4中,有一个可用的选项:ObjectContextOptions.LazyloadingenEnabled
将其设置为false应避免出现"循环引用"问题。但是,必须显式加载要包含的导航属性。
请参阅:http://msdn.microsoft.com/en-us/library/bb896272.aspx
据我所知,您不能序列化对象引用,但只能复制您可以尝试使用一个类似这样的脏黑客:
我所做的有点激进,但我不需要这个属性,因为它使讨厌的循环引用产生错误,所以我在序列化之前将其设置为空。
1 2 3 4 5 6 7 | SessionTickets result = GetTicketsSession(); foreach(var r in result.Tickets) { r.TicketTypes = null; //those two were creating the problem r.SelectedTicketType = null; } return Json(result); |
如果您确实需要您的属性,您可以创建一个不包含循环引用的ViewModel,但可能保留一些重要元素的ID,稍后您可以使用这些ID恢复原始值。
这周我也遇到了同样的问题,不能使用匿名类型,因为我需要实现一个要求
在确定
您需要决定哪个是"根"对象。假设机器是根,那么客户就是机器的子对象。当您序列化机器时,它会在JSON中将客户序列化为子对象,而当客户序列化时,它不会序列化它对机器的引用。当您的代码反序列化机器时,它将反序列化机器的客户子对象,并恢复从客户到机器的后引用。
大多数序列化库都提供了某种钩子来修改如何对每个类执行反序列化。您需要使用该钩子来修改machine类的反序列化,以恢复机器客户中的backreference。钩子的具体内容取决于您使用的JSON库。