JSON.NET的Self referencing loop detected with type的原因以及解決辦法
模型中有循環引用是很常見的。例如,以下模型顯示雙向導航屬性:
1: public class Category 2: { 3: public Category() 4: { 5: Products = new Collection<Product>(); 6: } 7: 8: public int Id { get; set; } 9: public string Name { get; set; } 10: public virtualICollection<Product> Products { get; set; } 11: } 12: 13: public class Product 14: { 15: public int Id { get; set; } 16: public string Name { get; set; } 17: public virtual Category Category { get; set; } 18: }
通過生成EF API控制器與Web API一起使用時,默認情況下不起作用。使用json.net序列化器序列化時會發生以下錯誤:
Self referencing loop detected for property ‘Category‘ with type
‘System.Data.Entity.DynamicProxies.Category_A97AC61AD05BA6A886755C779FD3F96E86FE903ED7C9BA9400E79162C11BA719‘.
Path ‘[0].Products[0]‘
發生此錯誤是因為序列化程序不知道如何處理循環引用。(在xml序列化程序中也出現類似的錯誤)
禁用代理並包含引用
EF代理不適用於POCO數據序列化。有幾種 解決方法。為了簡單起見,我們只是在數據上下文類中禁用它:
1: public CircularReferenceSampleContext() : base("name=CircularReferenceSampleContext") 2: { 3: Database.SetInitializer(new CircularReferenceDataInitializer()); 4: this.Configuration.LazyLoadingEnabled = false; 5: this.Configuration.ProxyCreationEnabled = false; 6: }
但是,在禁用代理之後,導航屬性不會被延遲加載。因此,從數據庫中檢索數據時必須包含參考。將腳手架控制器代碼更改為:
1: public IEnumerable <Product> GetProducts() 2: { 3: return db.Products.Include(p => p.Category).AsEnumerable(); 4: }
包含調用將包含所有記錄的參考數據。
修復1:全局忽略循環引用
json.net序列化器支持忽略全局設置的循環引用。一個快速解決方案是將下面的代碼放在WebApiConfig.cs文件中:
1: config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
簡單的修復將使序列化程序忽略會導致循環的引用。但是,它有局限性:
- 數據丟失循環參考信息
- 該修補程序僅適用於JSON.net
- 如果存在深度參考鏈,則無法控制參考級別
修復2:保留全局循環引用
第二個修復與第一個類似。只需將代碼更改為:
1: config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling 2: = Newtonsoft.Json.ReferenceLoopHandling.Serialize; 3: config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling 4: = Newtonsoft.Json.PreserveReferencesHandling.Objects;
數據形狀將在應用此設置後更改。
1: [{ “$ ID” :“1” , “類別”:{ “$ ID” :“2” , “產品”:[{ “$ ID” :“3” , “類別”:{ “$ REF “:”2“ },”ID“:2, ” 名稱“:”酸奶“ },{ ”$ REF“ :”1“ }],”ID“:1, ” 名稱“:”日記“ }, ” Id“:1,“名稱”:“全脂牛奶” },{ “$ ref”:“3” }]
$ id和$ ref保留所有引用,並使對象圖級別保持不變,但客戶端代碼需要知道形狀更改以消費數據,並且它僅適用於JSON.NET序列化程序。
修復3:忽略並保留參考屬性
此修補程序在模型類上裝飾屬性以控制模型或屬性級別上的序列化行為。忽略該屬性:
1: public class Category 2: { 3: public int Id { get; set; } 4: public string Name { get; set; } 5: 6: [JsonIgnore] 7: [IgnoreDataMember] 8: public virtual ICollection<Product> Products { get; set; } 9: }JsonIgnore用於JSON.NET,IgnoreDataMember用於XmlDCSerializer。
為了保持參考:
1: // Fix 3 2: [JsonObject(IsReference = true)] 3: public class Category 4: { 5: public int Id { get; set; } 6: public string Name { get; set; } 7: 8: // Fix 3 9: //[JsonIgnore] 10: //[IgnoreDataMember] 11: public virtual ICollection<Product> Products { get; set; } 12: } 13: 14: [DataContract(IsReference = true)] 15: public class Product 16: { 17: [Key] 18: public int Id { get; set; } 19: 20: [DataMember] 21: public string Name { get; set; } 22: 23: [DataMember] 24: public virtual Category Category { get; set; } 25: }
[JsonObject(IsReference = true)]適用於JSON.NET,[DataContract(IsReference = true)]適用於XmlDCSerializer。請註意:在類上應用DataContract後,您需要將DataMember添加到要序列化的屬性。
這些屬性可以應用於json和xml序列化器,並且可以為模型類提供更多的控制。
參考官方解決方案:https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7
JSON.NET的Self referencing loop detected with type的原因以及解決辦法