我有一个对象,它具有对另一个对象的循环引用。鉴于这些对象之间的关系,这是正确的设计。
说明
Machine => Customer => Machine
正如我所料,当我尝试使用Json序列化机器或客户对象时,我遇到了一个问题。我不确定的是如何解决这个问题,因为我不想打破Machine和Customer对象之间的关系。有什么方法可以解决这个问题?
修改
目前我正在使用Json method provided by the Controller base class。所以我正在做的序列化基本如下:
Json(machineForm);
答案 0 :(得分:57)
<强>更新强>
请勿尝试使用NonSerializedAttribute
,因为JavaScriptSerializer
显然忽略了它。
相反,请使用ScriptIgnoreAttribute
中的System.Web.Script.Serialization
。
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
方法时,它会遍历从Machine
到Customer
的关系,但不会尝试从Customer
返回{1}}至Machine
。
您的代码可以随心所欲地建立关系,但JavaScriptSerializer
(由Json
方法使用)将忽略它。
答案 1 :(得分:33)
我正在回答这个问题,尽管它的年龄很大,因为它是Google发布的“json.encode循环引用”的第3个结果(目前),虽然我不同意上面的答案(完全),但是使用ScriptIgnoreAttribute假设您不会在代码中的任何位置想要遍历某个JSON的另一个方向的关系。由于一个用例,我不相信锁定你的模型。
这确实激励我使用这个简单的解决方案。
由于您正在使用MVC中的View,因此您拥有Model,并且您只想将Model分配给控制器中的ViewData.Model,继续使用View中的LINQ查询来平滑数据。删除您想要的特定JSON的违规循环引用:
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);
或者如果机器 - &gt;客户关系是1 .. * - &gt; *然后尝试:
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);
答案 2 :(得分:10)
根据txl的答案你必须这样做 禁用延迟加载和代理创建,您可以使用常规方法获取数据。
示例:
//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);
}
答案 3 :(得分:4)
用于解决同样的问题。我创建了一个简单的扩展方法,将L2E对象“展平”为IDictionary。 JavaScript字典由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);
}
我的Action方法如下所示:
public JsonResult AsJson(int id) {
var data = Find(id);
var result = this.JsonFlatten(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
答案 4 :(得分:2)
在Entity Framework version 4中,有一个选项:ObjectContextOptions.LazyLoadingEnabled
将其设置为false应该避免“循环引用”问题。但是,您必须显式加载要包含的导航属性。
答案 5 :(得分:1)
据我所知,你不能序列化对象引用,但只有副本你可以尝试使用一些类似于这样的脏黑客:
答案 6 :(得分:0)
您需要确定哪个是“根”对象。假设机器是根,那么客户是机器的子对象。当您对机器进行序列化时,它会将客户序列化为JSON中的子对象,并且当客户被序列化时,它将 NOT 序列化它对机器的反向引用。当您的代码反序列化机器时,它将反序列化机器的客户子对象并将客户的反向引用恢复到机器。
大多数序列化库提供某种钩子来修改每个类的反序列化的方式。您需要使用该挂钩来修改机器类的反序列化,以便在机器的客户中恢复反向引用。究竟是什么钩子取决于你正在使用的JSON库。
答案 7 :(得分:0)
本周我也有同样的问题,并且无法使用匿名类型,因为我需要实现一个要求List<MyType>
的接口。在制作了显示所有与导航性的关系的图表之后,我发现MyType
与MyObject
具有双向关系,导致此循环引用,因为它们都相互保存。
在确定MyObject
确实不需要知道MyType
之后,从而使其成为单向关系,这个问题就解决了。
答案 8 :(得分:0)
我所做的是有点激进,但我不需要该属性,这会导致令人讨厌的循环引用错误,因此我在序列化之前将其设置为null。
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,以后可以用来恢复原始值。