我正在尝试做一个简单的JSON返回,但我遇到了问题,我有以下内容。
public JsonResult GetEventData()
{
var data = Event.Find(x => x.ID != 0);
return Json(data);
}
我得到一个HTTP 500,但是如此问题的标题所示。我也试过
var data = Event.All().ToList()
这给出了同样的问题。
这是一个错误还是我的实施?
答案 0 :(得分:170)
似乎您的对象层次结构中存在循环引用,JSON序列化程序不支持该引用。你需要所有的栏目吗?您只能在视图中选择所需的属性:
return Json(new
{
PropertyINeed1 = data.PropertyINeed1,
PropertyINeed2 = data.PropertyINeed2
});
这将使您的JSON对象更轻松,更易于理解。如果您有许多属性,AutoMapper可用于在{0}对象和视图对象之间进行automatically映射。
答案 1 :(得分:92)
我遇到了同样的问题,并由using Newtonsoft.Json;
var list = JsonConvert.SerializeObject(model,
Formatting.None,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
return Content(list, "application/json");
答案 2 :(得分:54)
这实际上是因为复杂对象是导致生成的json对象失败的原因。 它失败了,因为当对象被映射时,它映射了映射其父对象的子对象,从而进行循环引用。 Json需要花费无限的时间来序列化它,所以它可以防止异常的问题。
实体框架映射也会产生相同的行为,解决方案是丢弃所有不需要的属性。
只是说明最终答案,整个代码将是:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
new {
Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
}
, JsonRequestBehavior.AllowGet
);
}
如果您不希望Result
属性中的对象,也可以是以下内容:
public JsonResult getJson()
{
DataContext db = new DataContext ();
return this.Json(
(from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
, JsonRequestBehavior.AllowGet
);
}
答案 3 :(得分:13)
总结一下,有4个解决方案:
解决方案1:关闭DBContext的ProxyCreation并最终将其恢复。
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
bool proxyCreation = db.Configuration.ProxyCreationEnabled;
try
{
//set ProxyCreation to false
db.Configuration.ProxyCreationEnabled = false;
var data = db.Products.ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
finally
{
//restore ProxyCreation to its original state
db.Configuration.ProxyCreationEnabled = proxyCreation;
}
}
解决方案2:通过设置ReferenceLoopHandling使用JsonConvert忽略序列化设置。
//using using Newtonsoft.Json;
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.ToList();
JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);
return Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
以下两个解决方案是相同的,但使用模型更好,因为它的类型很强。
解决方案3:返回仅包含所需属性的模型。
private DBEntities db = new DBEntities();//dbcontext
public class ProductModel
{
public int Product_ID { get; set;}
public string Product_Name { get; set;}
public double Product_Price { get; set;}
}
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new ProductModel
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
解决方案4:返回一个新的动态对象,其中仅包含所需的属性。
private DBEntities db = new DBEntities();//dbcontext
public ActionResult Index()
{
try
{
var data = db.Products.Select(p => new
{
Product_ID = p.Product_ID,
Product_Name = p.Product_Name,
Product_Price = p.Product_Price
}).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(ex.Message);
}
}
答案 4 :(得分:7)
JSON与xml和其他各种格式一样,是一种基于树的序列化格式。如果您的对象中有循环引用,它将不会爱你,因为“树”将是:
root B => child A => parent B => child A => parent B => ...
通常有一些方法可以沿某条路径禁用导航;例如,使用XmlSerializer
,您可以将父属性标记为XmlIgnore
。我不知道这是否可以使用json序列化程序,也不知道DatabaseColumn
是否有合适的标记(非常不太可能,因为它需要引用每个序列化API)
答案 5 :(得分:4)
因为新的DbContext T4模板用于生成EntityFramework实体。为了能够执行更改跟踪,此模板使用代理模式,通过使用它们包装好的POCO。这会在使用JavaScriptSerializer进行序列化时导致问题。
那么2个解决方案是:
您可以通过在上下文配置
上设置代理来关闭代理的自动生成context.Configuration.ProxyCreationEnabled = false;
在下面的文章中做了很好的解释。
http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/
答案 6 :(得分:4)
避免直接转换表对象。如果在其他表之间设置关系,则可能会抛出此错误。 相反,您可以创建一个模型类,为类对象赋值,然后将其序列化。
答案 7 :(得分:4)
使用Newtonsoft.Json:在你的Global.asax Application_Start方法中添加以下行:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
答案 8 :(得分:4)
将[JsonIgnore]
添加到模型中的虚拟属性。
答案 9 :(得分:2)
如果答案很好,但我认为可以通过添加“架构”视角来改进它们。
<强>研究强>
MVC's Controller.Json
函数正在执行此任务,但在这种情况下提供相关错误非常差。通过使用Newtonsoft.Json.JsonConvert.SerializeObject
,错误确切地指定了触发循环引用的属性是什么。这在序列化更复杂的对象层次结构时特别有用。
适当的架构
永远不应该尝试序列化数据模型(例如EF模型),因为在序列化方面,ORM的导航属性是消亡的道路。数据流应如下:
Database -> data models -> service models -> JSON string
可以使用自动映射器(例如Automapper)从数据模型中获取服务模型。虽然这并不能保证缺乏循环引用,但正确的设计应该这样做:服务模型应该包含服务消费者所需要的内容(即属性)。
在极少数情况下,当客户端请求在不同级别上涉及相同对象类型的层次结构时,该服务可以创建具有parent-&gt;子关系的线性结构(仅使用标识符,而不是引用)。
现代应用程序倾向于避免一次加载复杂的数据结构,服务模型应该很小。 E.g:
答案 10 :(得分:1)
我使用修复程序,因为在MVC5视图中使用Knockout。
行动
return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));
功能
public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
{
TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
foreach (var item in Entity.GetType().GetProperties())
{
if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
item.SetValue(Entity_, Entity.GetPropValue(item.Name));
}
return Entity_;
}
答案 11 :(得分:0)
您可以注意到导致循环引用的属性。然后你可以做类似的事情:
private Object DeCircular(Object object)
{
// Set properties that cause the circular reference to null
return object
}
答案 12 :(得分:-1)
//first: Create a class as your view model
public class EventViewModel
{
public int Id{get;set}
public string Property1{get;set;}
public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
var events = await db.Event.Find(x => x.ID != 0);
List<EventViewModel> model = events.Select(event => new EventViewModel(){
Id = event.Id,
Property1 = event.Property1,
Property1 = event.Property2
}).ToList();
return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}
答案 13 :(得分:-1)
解决此问题的一个更简单的方法是返回一个字符串,并使用JavaScriptSerializer将该字符串格式化为json。
webBrowser1.Navigate("http://localhost/myproject/public/client/register?identity=" + getIdentity());
“选择”部分非常重要,它可以在视图中选择所需的属性。某些对象具有父级的引用。如果您不选择属性,如果您只是将表格作为一个整体,则可能会出现循环引用。
不要这样做:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
如果您不想要整个表格,请执行此操作:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.toList();
return j.Serialize(entityList );
}
这有助于使用更少的数据呈现视图,只需使用您需要的属性,并使您的网络运行更快。