1. ホーム
  2. javascript

[解決済み] 循環参照を持つJavaScriptオブジェクトの文字列化(JSONへの変換)

2023-06-05 16:23:41

質問

循環参照を含むJavaScriptオブジェクトの定義を持っています:それは親オブジェクトを参照するプロパティを持っています。

それはまた、サーバーに渡したくない関数を持っています。これらのオブジェクトをどのようにシリアライズおよびデシリアライズすればよいのでしょうか。

私は、これを行うための最良の方法は、Douglas Crockfordの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が戻ってくると仮定してデシリアライズするにはどうすればよいでしょうか。

どのように解決するのですか?

円形構造 のエラーは、オブジェクトのプロパティが直接オブジェクト自身である場合( a -> a ) または間接的に ( a -> b -> a ).

エラーメッセージを回避するには、JSON.stringifyが循環参照に遭遇したときに何をすべきかを指示します。 たとえば、ある人物が別の人物 ("parent") を指していて、それが元の人物を指している (かもしれない) 場合は、次のようにしてください。

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

の2番目のパラメータは stringify フィルタ関数 . ここでは、単に参照されたオブジェクトをその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;
  };
});

ちなみに、"とは別の属性名を選びます。 parent は多くの言語(および DOM)で予約語になっているからです。これは、将来的に混乱を引き起こす傾向があります...。