我尝试序列化从Entity Data Model .edmx自动生成的POCO类,当我使用以下代码:
JsonConvert.SerializeObject
我遇到了以下错误:
发生类型为System.data.entity的自引用循环检测错误。
我该怎么解决这个问题?
我尝试序列化从Entity Data Model .edmx自动生成的POCO类,当我使用以下代码:
JsonConvert.SerializeObject
我遇到了以下错误:
发生类型为System.data.entity的自引用循环检测错误。
我该怎么解决这个问题?
最佳解决方案来自Web API中的循环引用处理(本回答大部分内容均来自此处):
修复方法1:全局忽略循环引用
(我选择/尝试了这个解决方案,许多其他人也是如此)
Json.net序列化器有一个选项可以忽略循环引用。将以下代码放入
WebApiConfig.cs
文件中:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
简单的修复方法会使序列化程序忽略引用,从而导致循环。然而,它有以下限制:
如果您想在非API ASP.NET项目中使用此修复程序,请将上述行添加到Global.asax.cs
,但首先添加:
var config = GlobalConfiguration.Configuration;
如果您想在 .Net Core 项目中使用此功能,您可以将 Startup.cs
更改为:
var mvc = services.AddMvc(options =>
{
...
})
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
修复方法2:全局保留循环引用
第二种修复方法与第一种类似。只需将代码更改为:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
应用此设置后,数据形状将发生变化。
[
{
"$id":"1",
"Category":{
"$id":"2",
"Products":[
{
"$id":"3",
"Category":{
"$ref":"2"
},
"Id":2,
"Name":"Yogurt"
},
{
"$ref":"1"
}
],
"Id":1,
"Name":"Diary"
},
"Id":1,
"Name":"Whole Milk"
},
{
"$ref":"3"
}
]
$id和$ref保持了所有引用并使对象图的层级变得扁平,但客户端代码需要知道形状的变化以消费数据,它只适用于JSON.NET序列化器。
此修复装饰模型类上的属性以控制模型或属性级别的序列化行为。要忽略属性:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
JsonIgnore是用于JSON.NET的,而IgnoreDataMember是用于XmlDCSerializer的。 为保留引用:
// Fix 3
[JsonObject(IsReference = true)]
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
// Fix 3
//[JsonIgnore]
//[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
[DataContract(IsReference = true)]
public class Product
{
[Key]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual Category Category { get; set; }
}
JsonObject(IsReference = true)]
是用于 JSON.NET 的,而 [DataContract(IsReference = true)]
则是用于 XmlDCSerializer。请注意:在将 DataContract
应用于类之后,您需要将 DataMember
添加到要序列化的属性中。
这些属性既可以应用于 json 序列化器,也可以应用于 xml 序列化器,并且可以更好地控制模型类。
[JsonIgnore]
对我很有效。 - Nathan BeckReferenceLoopHandling.Error
(默认值)在遇到引用循环时会报错。这就是为什么你会收到异常。ReferenceLoopHandling.Serialize
适用于嵌套但不是无限的对象。ReferenceLoopHandling.Ignore
如果一个对象是它自己的子对象,则不会对其进行序列化。例如:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
如果您需要序列化一个嵌套无限的对象,您可以使用PreserveObjectReferences来避免StackOverflowException异常。
示例:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
选择对你正在序列化的对象有意义的选项。
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
使其正常工作。 - user1873471ReferenceLoopHandling.Serialize
将导致序列化程序进入无限递归循环并导致堆栈溢出。 - Brian RogersReferenceLoopHandling.Ignore
。 - viking解决方法是忽略循环引用并且不对它们进行序列化。这种行为在JsonSerializerSettings
中有具体规定。
使用重载的单一JsonConvert
:
JsonConvert.SerializeObject(YourObject, Formatting.Indented,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
);
全局设置,代码位于 Global.asax.cs 中的 Application_Start()
:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
最简单的方法是从nuget安装Json.NET,并将[JsonIgnore]
属性添加到类中的虚拟属性,例如:
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Project_ID { get; set; }
[JsonIgnore]
public virtual Project Project { get; set; }
虽然最近我创建的模型只包含我需要传递的属性,使它更轻便,不包含不必要的集合,并且在重新生成文件时也不会丢失我的更改。
using System.Buffers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
// beginning of Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings(){
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}, ArrayPool<char>.Shared));
});
}
Startup.cs
中的ConfigureServices
部分。https://learn.microsoft.com/en-us/ef/core/querying/related-data/serialization
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
如果您使用的是不带MVC的.NET Core 3.x - 5.0版本,则应为:
# startup.cs
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
对于 .NET 6.0,唯一的区别是现在它放在了 Program.cs
中。
# program.cs
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore);
如果您正在使用Entity Framework和数据库优先设计模式,则几乎必须进行此引用循环处理。
services.AddMvc()
会发生什么? - prisar如果您在使用NEWTONSOFTJSON进行序列化时遇到了循环问题,在我的情况下,我不需要修改global.asax或apiconfig文件。我只需使用JsonSerializesSettings并忽略循环处理即可。
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var lst = db.shCards.Where(m => m.CardID == id).ToList();
string json = JsonConvert.SerializeObject(lst, jss);
new Newtonsoft.Json.JsonSerializerSettings()
{ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});```
- Graham我们可以将这两行代码添加到DbContext类的构造函数中,以禁用自引用循环,例如:
public TestContext()
: base("name=TestContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
您也可以对属性应用属性。 [JsonProperty( ReferenceLoopHandling = ... )]
属性非常适合此用途。
例如:
/// <summary>
/// Represents the exception information of an event
/// </summary>
public class ExceptionInfo
{
// ...code omitted for brevity...
/// <summary>
/// An inner (nested) error.
/// </summary>
[JsonProperty( ReferenceLoopHandling = ReferenceLoopHandling.Ignore, IsReference = true )]
public ExceptionInfo Inner { get; set; }
// ...code omitted for brevity...
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(options =>
{
options.OutputFormatters.RemoveTypesOf<JsonOutputFormatter>();
var jsonOutputFormatter = new JsonOutputFormatter();
jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
options.OutputFormatters.Insert(0, jsonOutputFormatter);
});
}
Task
)时,由于忘记在前面加上await
语句,导致出现了这个错误。 - Uwe Keim