2013-03-09 13 views
11

から循環参照を解決します。私はそうのようjson.netからシリアル化されたJSONをお持ちの場合はJSONオブジェクト

User:{id:1,{Foo{id:1,prop:1}}, 
FooList{$ref: "1",Foo{id:2,prop:13}} 

を私はノックアウト出力にFooList以上のforeachを持つようにしたいが、私は$ので、続行するかどうかはわかりません物事は物事を投げることができます。

私は何とか使用しないでFooListでレンダリングされるすべてのFOOSを強制することです解決策を考えている:

PreserveReferencesHandling = PreserveReferencesHandling.Objects 

が、それは無駄なようだ..

+0

このためのもう一つの解決策: http://stackoverflow.com/questions/10747341/is-there-a-jquery-function-that-c​​an-take-a-ref- id-value-a-parsed-json-stri/12622112#12622112 – jvitor83

+0

も参照してください[JsonNetDecycle](https://bitbucket.org/smithkl42/jsonnetdecycle) –

答えて

13

ますJSONオブジェクトサーバーから受信しているのはCircular Referencesです。オブジェクトを使用する前に、オブジェクトからすべての$refプロパティを最初に削除する必要があります。つまり、$ref : "1"の代わりに、このリンクが指し示すオブジェクトを配置する必要があります。あなたのケースでは

は、そのIDがこのために1

であるあなたがDouglas Crockfords Plugin on github .Thereをチェックアウトする必要がありますあなたのための仕事をすることができますcycle.jsあるユーザーのオブジェクトを指しているかもしれません。

たり、次のコード(テストしていません)を使用することができます:それは場合に役立ちます私に教えてください

function resolveReferences(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 ("$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[refs[2]]; 
     // Notice that this throws if you put in a reference at top-level 
    } 
    return json; 
} 

を!

+0

すごい素晴らしいもの!! – FutuToad

+0

byid [id] = obj代入を(var id = ...代入の背後に)上に移動すると、refs配列のエントリがずっと少なくなります。私のオブジェクトのグラフでは、私は全く何も得ていません。 – Rolf

23

私はいくつかのバグを発見し、配列のサポートを実装しました:

function resolveReferences(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; 
} 
+0

あなたはあなたが何を変えたかをもっと説明できますか? – FutuToad

+1

- を追加しました。(Object.prototype.toString.call(obj)=== '[オブジェクト配列]'){...} - ほとんど最新の文字列は間違っていました: ref [0] [ref [1 ]] = byid [refs [2]]; ただし、次のようにする必要があります。 ref [0] [ref [1]] = byid [ref [2]]; - この文字列は です。obj [prop] = recurse(obj [prop]、prop、obj) obj [prop] = recurse(obj [prop]、prop、obj); –

+1

ありがとう!私はバグを検索するために多くの時間を費やしています! –

3

私はアレクサンダーワシリエフの答えで配列補正とのトラブルがありました。

私は彼の答えにコメントすることはできません(十分な評判のポイントを持っていない;-))ので、新しい回答を追加する必要がありました... (私はベストプラクティスとしてポップアップを持っていました答えとだけ元の質問に - あなたは、配列を検査し、プリミティブな値に遭遇している場合BOF)受け入れの実装で

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 
       return obj[i]; 
      if ("$ref" in obj[i]) 
       obj[i] = recurse(obj[i], i, obj); 
      else 
       obj[i] = recurse(obj[i], prop, obj); 
     } 
     return obj; 
    } 
+0

私のポストのコードを変更します。ありがとうございました! :D –

+1

しかし、Microsoft ASP.NET ODataのサーバー側実装の最新バージョンが、すでに返されたオブジェクトを参照するための "$ ref"の出力をサポートしていないため、プロダクションではこれ以上使用しません。マイクロソフトではフォーラムで、実装しないと語っている。 ;-( –

+0

配列ループで$ refの区別をする必要はありますか?次のインスタンスはチェックを行いますが、非参照の場合は、2番目のパラメータとして 'prop'の渡しが正しいとは思わない – Rolf

2

、あなたはその値を返し、その配列を上書きします。代わりに配列のすべての要素を調べ続け、最後に配列を返したいとします。

function resolveReferences(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; 
} 
0

私の解決策(同様の配列のために動作します):

使用:rebuildJsonDotNetObj(jsonDotNetResponse)

コード:

function rebuildJsonDotNetObj(obj) { 
    var arr = []; 
    buildRefArray(obj, arr); 
    return setReferences(obj, arr) 
} 

function buildRefArray(obj, arr) { 
    if (!obj || obj['$ref']) 
     return; 
    var objId = obj['$id']; 
    if (!objId) 
    { 
     obj['$id'] = "x"; 
     return; 
    } 
    var id = parseInt(objId); 
    var array = obj['$values']; 
    if (array && Array.isArray(array)) { 
     arr[id] = array; 
     array.forEach(function (elem) { 
      if (typeof elem === "object") 
       buildRefArray(elem, arr); 
     }); 
    } 
    else { 
     arr[id] = obj; 
     for (var prop in obj) { 
      if (typeof obj[prop] === "object") { 
       buildRefArray(obj[prop], arr); 
      } 
     } 
    } 
} 

function setReferences(obj, arrRefs) { 
    if (!obj) 
     return obj; 
    var ref = obj['$ref']; 
    if (ref) 
     return arrRefs[parseInt(ref)]; 

    if (!obj['$id']) //already visited 
     return obj; 

    var array = obj['$values']; 
    if (array && Array.isArray(array)) { 
     for (var i = 0; i < array.length; ++i) 
      array[i] = setReferences(array[i], arrRefs) 
     return array; 
    } 
    for (var prop in obj) 
     if (typeof obj[prop] === "object") 
      obj[prop] = setReferences(obj[prop], arrRefs) 
    delete obj['$id']; 
    return obj; 
} 
2

あなたが活用する場合、これは実際には非常に簡単ですJSON.parsereviverパラメータ:

// example JSON 
 
var j = '{"$id":"0","name":"Parent","child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},"nullValue":null}' 
 

 
function parseAndResolve(json) { 
 
    var refMap = {}; 
 

 
    return JSON.parse(json, function (key, value) { 
 
     if (key === '$id') { 
 
      refMap[value] = this; 
 
      // return undefined so that the property is deleted 
 
      return void(0); 
 
     } 
 

 
     if (value && value.$ref) { return refMap[value.$ref]; } 
 

 
     return value; 
 
    }); 
 
} 
 

 
console.log(parseAndResolve(j));

+0

こんにちは、これは素晴らしいですが、nullに設定されたオブジェクトフィールドのエラーがあります。 "if(value&$ value)if"(value && value。$ ref) "を置き換えてください: – Arcord

+0

@アコードそれを考え出してくれてありがとう、ありがとう!回答が更新されました。 – JLRishe

関連する問題