web-dev-qa-db-ja.com

循環参照を使用してJavaScriptオブジェクトを文字列化(JSONに変換)

循環参照を含むJavaScriptオブジェクト定義を持っています。親オブジェクトを参照するプロパティがあります。

また、サーバーに渡されたくない関数もあります。これらのオブジェクトをどのようにシリアライズおよびデシリアライズしますか?

これを行うための最良の方法は、ダグラス・クロックフォードのstringifyを使用することだと読んだことがあります。ただし、Chromeで次のエラーが表示されます。

TypeError:循環構造をJSONに変換

コード:

function finger(xid, xparent){
    this.id = xid;
    this.xparent;
    //other attributes
}

function arm(xid, xparent){
    this.id = xid;
    this.parent = xparent;
    this.fingers = [];

    //other attributes

    this.moveArm = function() {
        //moveArm function details - not included in this testcase
        alert("moveArm Executed");
    }
}

 function person(xid, xparent, xname){
    this.id = xid;
    this.parent = xparent;
    this.name = xname
    this.arms = []

    this.createArms = function () {
        this.arms[this.arms.length] = new arm(this.id, this);
    }
}

function group(xid, xparent){
    this.id = xid;
    this.parent = xparent;
    this.people = [];
    that = this;

    this.createPerson = function () {
        this.people[this.people.length] = new person(this.people.length, this, "someName");
        //other commands
    }

    this.saveGroup = function () {
        alert(JSON.stringify(that.people));
    }
}

これは、この質問に対して作成したテストケースです。このコードにはエラーがありますが、基本的にはオブジェクト内にオブジェクトがあり、オブジェクトが作成されたときに親オブジェクトが何であるかを示すために各オブジェクトに参照が渡されます。各オブジェクトには、文字列化されたくない関数も含まれています。 Person.Nameなどのプロパティが必要です。

サーバーに送信する前にシリアライズし、同じJSONが返されると仮定してデシリアライズするにはどうすればよいですか?

66
user1012500

循環構造エラーは、オブジェクト自体のオブジェクトであるオブジェクトのプロパティがある場合に発生します(a -> a)または間接的(a -> b -> a)。

エラーメッセージを回避するには、JSON.stringifyに循環参照が発生した場合の処理​​を指示します。たとえば、元の人を指している(または持っていない)別の人(「親」)を指している人がいる場合、次の手順を実行します。

JSON.stringify( that.person, function( key, value) {
  if( key == 'parent') { return value.id;}
  else {return value;}
})

stringifyの2番目のパラメーターは、フィルター関数です。ここでは、参照されるオブジェクトをそのIDに変換するだけですが、循環参照を解除するために自由に実行できます。

次のコードを使用して上記のコードをテストできます。

function Person( params) {
  this.id = params['id'];
  this.name = params['name']; 
  this.father = null;
  this.fingers = [];
  // etc.
}

var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him; 
JSON.stringify(me); // so far so good

him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
  if(key == 'father') { 
    return value.id;
  } else {
    return value;
  };
});

ところで、多くの言語(およびDOM)で予約語であるため、「parent」とは異なる属性名を選択します。これは、今後混乱を引き起こす傾向があります...

103
tocker

dojo はJSONの循環参照を次の形式で表すことができるようです:{"id":"1","me":{"$ref":"1"}}

以下に例を示します。

http://jsfiddle.net/dumeG/

require(["dojox/json/ref"], function(){
    var me = {
        name:"Kris",
        father:{name:"Bill"},
        mother:{name:"Karen"}
    };
    me.father.wife = me.mother;
    var jsonMe = dojox.json.ref.toJson(me); // serialize me
    alert(jsonMe);
});​

生産物:

{
   "name":"Kris",
   "father":{
     "name":"Bill",
     "wife":{
          "name":"Karen"
      }
   },
   "mother":{
     "$ref":"#father.wife"
   }
}

注:dojox.json.ref.fromJsonメソッドを使用して、これらの循環参照オブジェクトを逆シリアル化することもできます。

その他のリソース:

循環参照がある場合でもDOMノードをJSONにシリアル化する方法

JSON.stringifyは循環参照を表すことができません

10
Brandon Boone

JSONで循環参照を処理するのに適したモジュールが2つ見つかりました。

  1. CircularJSON https://github.com/WebReflection/circular-json この出力は、.parse()への入力として使用できます。 Browsers&Node.jsでも動作します: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
  2. Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe これは読みやすいかもしれませんが、.parseには使用できず、Node.jsでのみ利用可能です

これらのいずれかがニーズを満たす必要があります。

5
nevf

特定の状況ではリモートデバッグが不可能だったため、複雑なオブジェクトをページに記録する必要があるため、このスレッドで発生しました。 Douglas Crockford(JSONのインセプター)が所有するcycle.jsが見つかりました。これは、解析後に再接続できるように、文字列として循環参照に注釈を付けます。リサイクルされたディープコピーは、JSON.stringifyを通過しても安全です。楽しい!

https://github.com/douglascrockford/JSON-js

cycle.js:このファイルにはJSON.decycleとJSON.retrocycleの2つの関数が含まれています。これらの関数は、JSONで周期的な構造とデータをエンコードし、それらを復元することを可能にします。これは、ES5では提供されない機能です。 JSONPathは、リンクを表すために使用されます。

2
Matt Evans