Entity Data Model .edmxから自動的に生成されたPOCOクラスを使用したときにシリアライズしようとしました
JsonConvert.SerializeObject
次のようなエラーが出ました。
エラーSystem.data.entityタイプで自己参照ループが検出されました。
どうやってこの問題を解決しますか?
それが最善の解決策でした https://code.msdn.Microsoft.com/Loop-Reference-handling-in-caaffaf7
(他の多くの人がそうであるように、私はこれを選んだ/試した)
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 projectでこれを使用したい場合は、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; }
}
JSON.NETの場合はJsonObject(IsReference = true)]
is、XmlDCSerializerの場合は[DataContract(IsReference = true)]
です。注:クラスにDataContract
を適用した後、直列化したいプロパティーにDataMember
を追加する必要があります。
属性はjsonとxmlの両方のシリアライザに適用することができ、モデルクラスに対してより多くのコントロールを与えます。
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
});
直列化しているオブジェクトにとって意味のあるものを選択してください。
修正は、ループ参照を無視し、それらを直列化しないことです。この振る舞いは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; }
最近は、渡したいプロパティだけを使用してモデルを作成し、軽量化し、不要なコレクションを含めず、生成されたファイルを再構築しても変更内容を失わないようにしています...
.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));
});
}
この2行をDbContextクラスのコンストラクターに追加して、自己参照ループを無効にすることができます。
public TestContext()
: base("name=TestContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
.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
);
...
}
ループの問題が発生したときに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);
プロパティに属性を適用することもできます。 [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
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);
});
}
私にとっては私は違う道を行かなければなりませんでした。 JSON.Netシリアライザを修正しようとする代わりに、私は自分のデータコンテキストで遅延ロードの後に行かなければなりませんでした。
これを私のベースリポジトリに追加しました。
context.Configuration.ProxyCreationEnabled = false;
"context"オブジェクトは、依存性注入を使用するので、私が私のベースリポジトリで使用するコンストラクタパラメータです。代わりに、データコンテキストをインスタンス化する場所ならどこでもProxyCreationEnabledプロパティを変更できます。
http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html
私はこの例外がありました、そして私の実用的な解決策は簡単で簡単です、
JsonIgnore属性を追加して、Referencedプロパティを無視します。
[JsonIgnore]
public MyClass currentClass { get; set; }
シリアル化解除したら、プロパティをリセットします。
Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
{
Source.MyClass = item;
}
newtonsoft.Jsonを使用します。
これをWebApiConfig.cs
クラスで使用します。
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
チーム:
これはASP.NET Coreで動作します。上記の課題は、どのように「設定を無視するように設定する」かです。アプリケーションの設定方法によっては、非常に困難な場合があります。これは私のために働いたものです。
これはあなたのpublic void ConfigureServices(IServiceCollection services)セクションに置くことができます。
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
人々はすでに[JsonIgnore]がクラスの仮想プロパティに追加されることについてすでに話しています、例えば:
[JsonIgnore]
public virtual Project Project { get; set; }
私はまた別のオプション、[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]を共有します。
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
カスタム設定JsonSerializerSettingsで解決した私の問題:
services.AddMvc(
// ...
).AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Serialize;
opt.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
});
単にConfiguration.ProxyCreationEnabled = false;
をコンテキストファイルの中に置くだけです。これで問題は解決します。
public demEntities()
: base("name=demEntities")
{
Configuration.ProxyCreationEnabled = false;
}