1. ホーム
  2. c#

[解決済み] JSON.NET エラー タイプの自己参照ループが検出されました。

2022-03-18 18:30:01

質問

エンティティデータモデル.edmxから自動生成されたPOCOクラスをシリアライズしようとしたのですが、その際に

JsonConvert.SerializeObject 

以下のようなエラーが発生しました。

エラー System.data.entity 型の自己参照ループが検出されました。

この問題を解決するにはどうしたらよいですか?

どのように解決するのですか?

これがベストな解決策だった https://docs.microsoft.com/en-us/archive/blogs/hongyes/loop-reference-handling-in-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 コア プロジェクトでは Startup.cs としています。

  var mvc = services.AddMvc(options =>
        {
           ...
        })
        .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

修正2: グローバルに循環参照を保持する

この2つ目の修正は、1つ目の修正と同様です。コードを次のように変更するだけです。

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 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の両方のシリアライザに適用することができ、モデル・クラスにより多くの制御を与えることができます。