web-dev-qa-db-ja.com

JSON.NETエラー自己参照ループがtypeに対して検出されました

Entity Data Model .edmxから自動的に生成されたPOCOクラスを使用したときにシリアライズしようとしました

JsonConvert.SerializeObject 

次のようなエラーが出ました。

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

どうやってこの問題を解決しますか?

410
NevenHuynh

それが最善の解決策でした https://code.msdn.Microsoft.com/Loop-Reference-handling-in-caaffaf7

修正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 projectでこれを使用したい場合は、Startup.csを次のように変更できます。

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

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

この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 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; } 
       }

JSON.NETの場合はJsonObject(IsReference = true)]is、XmlDCSerializerの場合は[DataContract(IsReference = true)]です。注:クラスにDataContractを適用した後、直列化したいプロパティーにDataMemberを追加する必要があります。

属性はjsonとxmlの両方のシリアライザに適用することができ、モデルクラスに対してより多くのコントロールを与えます。

390
Bishoy Hanna

JsonSerializerSettingsを使用する

  • 参照ループが発生すると、ReferenceLoopHandling.Error(デフォルト)はエラーになります。 これが、例外が発生する理由です。
  • ReferenceLoopHandling.Serializeは、オブジェクトがネストされていて無期限ではない場合に便利です。
  • ReferenceLoopHandling.Ignoreは、それが自身の子オブジェクトである場合、そのオブジェクトをシリアル化しません。

例:

JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented, 
new JsonSerializerSettings { 
        ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});

無期限にネストしたオブジェクトをシリアル化する必要がある場合は、StackOverflowExceptionを回避するために PreserveObjectReferences を使用できます。

例:

JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented, 
new JsonSerializerSettings { 
        PreserveReferencesHandling = PreserveReferencesHandling.Objects
});

直列化しているオブジェクトにとって意味のあるものを選択してください。

参照 http://james.newtonking.com/json/help/

416
DalSoft

修正は、ループ参照を無視し、それらを直列化しないことです。この振る舞いは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
};

参照: https://github.com/JamesNK/Newtonsoft.Json/issues/78

47
smockle

これを行う最も簡単な方法は、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; }

最近は、渡したいプロパティだけを使用してモデルを作成し、軽量化し、不要なコレクションを含めず、生成されたファイルを再構築しても変更内容を失わないようにしています...

38
Sam Jones

.NET Core 1.0では、Startup.csファイルでこれをグローバル設定として設定できます。

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));
        });
    }
21
Caleb

この2行をDbContextクラスのコンストラクターに追加して、自己参照ループを無効にすることができます。

public TestContext()
        : base("name=TestContext")
{
    this.Configuration.LazyLoadingEnabled = false;
    this.Configuration.ProxyCreationEnabled = false;
}
7
Sanjay Nishad

.NET Core 2.0を使用している場合は、Startup.csのConfigureServicesセクションを更新します。

https://docs.Microsoft.com/ja-jp/ef/core/querying/related-data#related-data-and-serialization

public void ConfigureServices(IServiceCollection services)
{
...

services.AddMvc()
    .AddJsonOptions(
        options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    );

...
}
5
Dave

ループの問題が発生したときにNEWTONSOFTJSONを使用してシリアル化するには、私の場合はglobal.asaxまたはapiconfigを変更する必要はありませんでした。 Looping処理を無視して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);
4
Carlos Barini

プロパティに属性を適用することもできます。 [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...    
}

助けになればと思います、Jaans

4
Jaans

MVC 6でループ参照を無視し、それらをグローバルに直列化しないようにするには、startup.csで次のようにします。

    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);
        });
    }
4
GerardBeckerleg

私にとっては私は違う道を行かなければなりませんでした。 JSON.Netシリアライザを修正しようとする代わりに、私は自分のデータコンテキストで遅延ロードの後に​​行かなければなりませんでした。

これを私のベースリポジトリに追加しました。

context.Configuration.ProxyCreationEnabled = false;

"context"オブジェクトは、依存性注入を使用するので、私が私のベースリポジトリで使用するコンストラクタパラメータです。代わりに、データコンテキストをインスタンス化する場所ならどこでもProxyCreationEnabledプロパティを変更できます。

http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html

2
Xipooo

私はこの例外がありました、そして私の実用的な解決策は簡単で簡単です、

JsonIgnore属性を追加して、Referencedプロパティを無視します。

[JsonIgnore]
public MyClass currentClass { get; set; }

シリアル化解除したら、プロパティをリセットします。

Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
        {
            Source.MyClass = item;
        }

newtonsoft.Jsonを使用します。

2
Mayer Spitzer

これをWebApiConfig.csクラスで使用します。

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
2
Anand Kumar

チーム:

これはASP.NET Coreで動作します。上記の課題は、どのように「設定を無視するように設定する」かです。アプリケーションの設定方法によっては、非常に困難な場合があります。これは私のために働いたものです。

これはあなたのpublic void ConfigureServices(IServiceCollection services)セクションに置くことができます。

services.AddMvc().AddJsonOptions(opt => 
        { 
      opt.SerializerSettings.ReferenceLoopHandling =
      Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });
1
FlyingV

人々はすでに[JsonIgnore]がクラスの仮想プロパティに追加されることについてすでに話しています、例えば:

[JsonIgnore]
public virtual Project Project { get; set; }

私はまた別のオプション、[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]を共有します。

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
1
Ali Raza

カスタム設定JsonSerializerSettingsで解決した私の問題:

services.AddMvc(
  // ...
               ).AddJsonOptions(opt =>
                 {
                opt.SerializerSettings.ReferenceLoopHandling =
                    Newtonsoft.Json.ReferenceLoopHandling.Serialize;
                opt.SerializerSettings.PreserveReferencesHandling =
                    Newtonsoft.Json.PreserveReferencesHandling.Objects;
                 });
0
AminGolmahalle

単にConfiguration.ProxyCreationEnabled = false;をコンテキストファイルの中に置くだけです。これで問題は解決します。

public demEntities()
    : base("name=demEntities")
{
    Configuration.ProxyCreationEnabled = false;
}
0
Fravius Kalisa