JSON和循环引用异常

问题描述:

我有具有圆形参照其他对象的对象。鉴于这些对象之间的关系,这是正确的设计。JSON和循环引用异常

来说明

Machine => Customer => Machine 

不愧我遇到一个问题,当我尝试使用JSON序列化机或客户对象。什么我不确定的是如何解决这个问题,因为我不想打破机和客户对象之间的关系。有什么解决这个问题的选择?

编辑

目前我使用Json method provided by the Controller base class。所以我做了序列化是基本的:

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 
    // ... 
} 

这样,当你折腾MachineJson方法,它会遍历从Machine的关系Customer但不会尝试回去从CustomerMachine

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

+0

我将不得不提供一个镜头,但不幸的是这是来自一个域/核心对象,我不确定我想引入对该项目的System.Web.Script.Serialization的引用。我将这些核心对象封装在一个模型中,我想我可以使用'ScriptIgnore',但在那时我可以删除循环引用。此解决方案似乎提供了类似的解决方案,以匿名类型http://*.com/questions/372955/best-way-to-filter-domain-objects-for-json-output-in-an-asp-net -mvc-application/372977#372977 – ahsteele 2010-01-05 01:46:35

+0

也可以。你应该对你的问题/评论有具体的说明 - 说你不能/不会删除循环引用不同于说模型中你不能/不会改变任何东西。我经常将匿名类型传递给'Json',我在这里看到的唯一问题是,如果您有多个方法返回JSON'ed'Machine',则需要记住每次“过滤”它。我眼中的巧克力与香草 - 它们都是不错的选择。 – Aaronaught 2010-01-05 02:25:01

+0

@Aaron了解。不确定我不能/不会确定它*感觉*是否正确地将引用添加到核心/域项目,以便为特定的UI情况提供此解决方案。我喜欢使用NonSerializedAttribute作为System名称空间中的解决方案。这似乎是JavaScriptSerializer忽略它的一个缺点。 – ahsteele 2010-01-05 02:48:23

因为,据我所知,你不能序列化对象的引用,但只复制你可以尝试使用了一下肮脏的黑客工具,是这样的:

当你反序列化JSON的代码,那么你可以运行在它上面的一个简单的函数,这些转变的ID为正确引用
  1. 客户应序列的机床参考作为机器的ID
+0

当然,但不会将机器信息传递到查询页面,并且需要额外的查询才能获得机器属性。 – ahsteele 2010-01-04 23:36:11

您需要决定这是“根”对象。假设机器是根,那么客户就是机器的一个子对象。当您连载机器时,它会将客户作为JSON中的子对象序列化,并且当客户序列化时,它会将其反向引用到机器上。当你的代码deserialises机器,它会deserialise机器的客户子对象以及恢复从客户机背参考。

大多数序列化库提供某种钩子来修改如何为每个类执行反序列化。你需要使用挂钩来修改deserialisation为机器类恢复机器的客户反向引用。究竟是什么钩子取决于你正在使用的JSON库。

用于有同样的问题。我创建了一个简单的扩展方法,将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); 
    } 

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

它的确激励我使用这个简单的解决方案。

由于您在MVC中的视图中工作,因此您拥有该模型,并且您只需将该模型分配给控制器中的ViewData.Model,继续在视图中使用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); 

或者在机床 - >客户关系是1 .. * - > *然后尝试:

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); 

Entity Framework version 4,有一个选项可用:ObjectContextOp tions.LazyLoadingEnabled

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

见:http://msdn.microsoft.com/en-us/library/bb896272.aspx

+0

您也可能在某些情况下也必须禁用代理生成功能。 – 2011-05-12 02:34:00

+0

在下面添加了一个示例:http://*.com/a/17068227/605586 – Thomas 2013-06-12 14:34:31

基于通心络的回答,你必须 禁用延迟加载和代理的创建,你可以使用正常的方法来获取数据。

例子:

//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); 
} 
+0

谢谢 - 这是我最喜欢的解决方案,因为它可以防止不必要的框架计算,而不是使用更多的代码“退出”到子集中不必要的工作执行后的数据。 – Stephen 2015-05-28 19:33:48

+0

HFS花了我3天才找到这个,但非常感谢你张贴这个解决方案! – user3320597 2016-05-31 14:38:33

我有同样的问题,这个星期为好,因为我需要实现一个接口要求一个List<MyType>不能使用匿名类型。在制作一个图表显示所有关系与导航能力之后,我发现MyTypeMyObject之间存在双向关系,这导致了此循环引用,因为它们都互相保存。

在确定MyObject确实不需要知道MyType,并由此使其成为单向关系之后,此问题得以解决。

我所做的是有点激进,但我不需要属性,这使得令人讨厌的循环引用引起的错误,所以我已经在序列化之前将它设置为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); 

如果你真的需要你的属性,你可以创建一个不成立循环引用视图模型,但也许保持重要元素的一些标识,那你以后可以用于恢复原始值。