Newtonsoft.Jsonを使用してASP.NETWebAPIコントローラーからデータを正しくシリアル化するのに問題があります。
これが私がしていることですthink起こっています-私が間違っているなら私を訂正してください。特定の状況下(特にデータに循環参照がない場合)では、すべてが期待どおりに機能します。つまり、入力されたオブジェクトのリストがシリアル化されて返されます。モデルに循環参照を引き起こすデータを導入すると(以下で説明し、PreserveReferencesHandling.Objects
が設定されている場合でも)、循環参照を持つ最初のオブジェクトに至るまでのリストの要素のみが、クライアントがシリアル化する方法でシリアル化されます。 「一緒に働く」ことができます。 「までの要素」は、シリアライザーに送信する前に順序が異なる場合、データ内の任意の要素にすることができますが、少なくとも1つは、クライアントが「処理」できる方法でシリアル化されます。空のオブジェクトは、Newtonsoft参照({$ref:X}
)としてシリアル化されます。
たとえば、次のようなナビゲーションプロパティを備えたEFモデルがある場合:
私のglobal.asaxで:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
Entity Frameworkを使用して行っている基本的なクエリは次のとおりです(遅延読み込みがオフになっているため、ここにはプロキシクラスはありません)。
[HttpGet]
[Route("starting")]
public IEnumerable<Balance> GetStartingBalances()
{
using (MyContext db = new MyContext())
{
var data = db.Balances
.Include(x => x.Source)
.Include(x => x.Place)
.ToList()
return data;
}
}
これまでのところ、data
が入力されています。
循環参照がない場合、人生は壮大です。ただし、同じBalance
またはSource
を持つ2つのPlace
エンティティがあるとすぐに、シリアル化により、最上位の遅いBalance
オブジェクトが回転します。 Balances
またはSource
オブジェクトのPlace
プロパティですでにシリアル化されているため、本格的なオブジェクトではなく、Newtonsoft参照に戻ることをリストします。
[{"$id":"1","BalanceID":4,"SourceID":2,"PlaceID":2 ...Omitted for clarity...},{"$ref":"4"}]
これに伴う問題は、私たち人間が何が起こっているのかを理解していても、クライアントが{$ref:4}
をどうするかを知らないことです。私の場合、これは、AngularJSを使用してこのJSONを使用した残高のリスト全体をng-repeat
することができないことを意味します。これは、それらがすべてBalance
プロパティを持つ真のBalance
オブジェクトではないためです縛る。同じ問題を抱えるユースケースは他にもたくさんあると思います。
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects
をオフにすることはできません。他の多くのものが壊れてしまうためです(これは、ここや他の場所にある100の他の質問で十分に文書化されています)。
Web APIコントローラーのエンティティを調べて実行する以外に、これに対するより良い回避策はありますか?
Balance.Source.Balances = null;
循環参照を壊すためにすべてのナビゲーションプロパティに?それも正しくないように思われるからです。
はい、PreserveReferencesHandling.Objects
を使用することは、循環参照を使用してオブジェクトグラフをシリアル化するための実際の最良の方法です。これは、最もコンパクトなJSONを生成し、オブジェクトグラフの参照構造を実際に保持するためです。つまり、JSONをオブジェクトに逆シリアル化すると($id
および$ref
表記を理解するライブラリを使用して)、特定のオブジェクトへの各参照は、ではなく、そのオブジェクトの同じインスタンスを指します。同じデータを持つ複数のインスタンスを持つ。
あなたの場合、問題は、クライアント側のパーサーがJson.Netによって生成された$id
および$ref
表記を理解しないため、参照が解決されないことです。これは、JavaScriptメソッドを使用して、JSONを逆シリアル化した後にオブジェクト参照を再構築することで修正できます。例については、 ここ および ここ を参照してください。
状況によっては、ReferenceLoopHandling
をIgnore
に設定する代わりに、シリアル化時にPreserveReferencesHandling
をObjects
に設定することもできます。ただし、これは完全な解決策ではありません。 ReferenceLoopHandling.Ignore
とPreserveReferencesHandling.Objects
の使用の違いの詳細な説明については、 この質問 を参照してください。