"JSON.NET错误:检测到类型的自我引用循环"
"JSON.NET错误:检测到类型的自我引用循环"
我尝试对从实体数据模型中自动生成的POCO类进行序列化,当我使用
JsonConvert.SerializeObject
时,我遇到了以下错误:
发生类型为System.data.entity的自我引用循环检测错误。
我该如何解决这个问题?
使用JsonSerializerSettings
ReferenceLoopHandling.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 });
选择适合您要序列化的对象的内容。
最佳解决方案来自于 Web API中的循环引用处理(本回答的大部分内容都是从这里复制来的):
解决方法1: 全局忽略循环引用
(我已选择/尝试了这个方法,和许多其他人一样)
json.net序列化器有一个选项可以忽略循环引用。在
WebApiConfig.cs
文件中放置以下代码:config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
这种简单的解决方法将使序列化器忽略引用,这将导致循环引用。然而,它有一些限制:
- 数据失去了循环引用信息
- 只适用于JSON.net
- 如果存在深度引用链,则无法控制引用的级别
如果您想在非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序列化器。
修复3:忽略和保留参考属性
这种修复是通过在模型类上装饰属性来控制模型或属性级别上的序列化行为。要忽略属性:
public class Category { public int Id { get; set; } public string Name { get; set; } [JsonIgnore] [IgnoreDataMember] public virtual ICollectionProducts { 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 ICollectionProducts { 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 序列化器,并在模型类上提供了更多控制。