この回答「無効にして回避するだけ」を見るのにうんざりしています。それは怠惰で間違っており、このデータを保持することの有用性を信じられないほど還元しています。
これは 3 つの部分からなる答えです。最初の部分は、コレクション参照を無効にし、オブジェクトのみを保持することです。
私の場合、コレクション参照を共有し、追加のコードが乱雑になるというシナリオは考えられませんでした。コレクション参照が必要な場合は、CONSUMING FE ロジックを記述します。無効にするだけではいけません。たまたま要らなかっただけです。
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
第二弾はこちら。別の回答からこれを取得し、欠落していた微調整を追加しました。(これは元のJSON オブジェクトからの循環参照の解決です):
function resolveReferences(json) {
if (!json)
return json;
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
continue;
else if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
2 番目の部分は、データを送り返す前に循環依存関係を再処理して再確立することです。私のFEはAngularアプリなので、ajax投稿の前に注入されたHTTPインターセプターでこれを個人的に呼び出しています(「typeName」というプロパティをチェックしていることに注意してください。そのプロパティを持つすべてのオブジェクトはバックエンドでデシリアライズされます)。
function processReferenceEstablishment(data) {
if (!data || typeof (data) === 'function')
return undefined;
var processData = [];
var tiers = {};
var refKey = 1;
if (Array.isArray(data)) {
data = jQuery.extend([], data);
} else {
data = jQuery.extend({}, data);
}
var retVal = (function recurse(obj, tier) {
tiers[tier] = tiers[tier] || {};
tiers[tier].width = tiers[tier].width ? tiers[tier].width + 1 : 1;
if (obj) {
if (typeof obj !== 'object' || typeof (obj) === 'function') {
return obj;
}
for (var key in data) {
var val = data[key];
if (key.indexOf("$") > -1 || typeof(val) === 'function') {
delete data[key];
}
}
if (Array.isArray(obj)) {
obj = jQuery.extend([], obj);
for (var ix = 0; ix < obj.length; ix++) {
obj[ix] = recurse(obj[ix], tier);
}
}
else if ('typeName' in obj) {
if (obj.skipSend) {
return undefined;
}
obj = jQuery.extend({}, obj);
var found = false;
for (var pdIx = 0; pdIx < processData.length; pdIx++) {
var item = processData[pdIx];
if (item.id === obj.id && item.typeName === obj.typeName) {
found = true;
if (!item.jsonTier || item.jsonTier > tier) {
item.jsonTier = tier;
item.jsonWidth = tiers[tier].width;
}
if (tier === item.jsonTier && item.jsonWidth > tiers[tier].width) {
item.jsonWidth = tiers[tier].width;
}
if (tier > item.jsonTier || (tier === item.jsonTier && item.jsonWidth < tiers[tier].width)) {
return { $ref: item.$id.toString() };
}
break;
}
}
if (!found) {
obj.$id = refKey;
refKey++;
processData.push({$id:obj.$id, id: obj.id, typeName: obj.typeName, jsonTier: tier, jsonWidth: tiers[tier].width });
}
var keys = Object.keys(obj);
keys.sort();
for (var key in keys) {
key = keys[key];
obj[key] = recurse(obj[key], tier + 1);
}
}
}
return obj;
})(data, 1);
retVal = (function recurse(obj, tier) {
tiers[tier] = tiers[tier] || {};
tiers[tier].destWidth = tiers[tier].destWidth ? tiers[tier].destWidth + 1 : 1;
if (typeof obj !== 'object' || !obj) {
return obj;
}
if (Array.isArray(obj)) {
for (var ix = 0; ix < obj.length; ix++) {
obj[ix] = recurse(obj[ix], tier);
}
}
else if ('typeName' in obj) {
var found = false;
for (var pdIx = 0; pdIx < processData.length; pdIx++) {
var item = processData[pdIx];
if (item.id === obj.id && item.typeName === obj.typeName) {
found = true;
if (item.jsonTier < tier || (item.jsonTier === tier && item.jsonWidth < tiers[tier].destWidth)) {
return { $ref: item.id.toString() };
}
}
}
var keys = Object.keys(obj);
keys.sort();
for (var key in keys) {
key = keys[key];
obj[key] = recurse(obj[key], tier + 1);
}
}
return obj;
})(retVal, 1);
return retVal;
}
さて、明確にするために。$ref と $id の再確立はしばしば順不同で行われ、null を返すバックエンドで奇妙なコレクション オブジェクト参照またはナビゲーション プロパティ オブジェクト参照を取得します。私はまだそれに取り組んでいます。NewtonSoft.JSON ライブラリをプルダウンしてばらばらにして、オブジェクト グラフを処理する順序を決定し、デシリアライズ後に null 参照を削除できるようにする必要があります。
私にとって、これは怠惰のためにタオルを投げて循環参照を無効にするのではなく、本当の解決策に向かっています。