85

循環参照を含む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が返されると仮定して逆シリアル化するにはどうすればよいですか?

4

6 に答える 6

132

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

エラーメッセージを回避するには、循環参照に遭遇したときにJSON.stringifyに何をするかを指示します。たとえば、元の人を指している(または指していない)別の人(「親」)を指している人がいる場合は、次のようにします。

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)で予約語であるため、「」とは異なる属性名を選択します。これは、将来的に混乱を引き起こす傾向があります...

于 2012-09-30T07:14:24.780 に答える
10

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は循環参照を表すことができません

于 2012-05-01T03:07:03.357 に答える
10

いいえ-lib

以下の置換を使用て、重複/循環参照オブジェクトへの文字列参照( json-pathと同様)を使用してjsonを生成します

let s = JSON.stringify(obj, refReplacer());

function refReplacer() {
  let m = new Map(), v= new Map(), init = null;

  return function(field, value) {
    let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field); 
    let isComplex= value===Object(value)
    
    if (isComplex) m.set(value, p);  
    
    let pp = v.get(value)||'';
    let path = p.replace(/undefined\.\.?/,'');
    let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
    
    !init ? (init=value) : (val===init ? val="#REF:$" : 0);
    if(!pp && isComplex) v.set(value, path);
   
    return val;
  }
}




// ---------------
// TEST
// ---------------

// gen obj with duplicate references
let a = { a1: 1, a2: 2 };
let b = { b1: 3, b2: "4" };
let obj = { o1: { o2:  a  }, b, a }; // duplicate reference
a.a3 = [1,2,b];                      // circular reference
b.b3 = a;                            // circular reference


let s = JSON.stringify(obj, refReplacer(), 4);

console.log(s);

そして、そのような「ref-json」からオブジェクトを再生成するための次のパーサー関数

function parseRefJSON(json) {
  let objToPath = new Map();
  let pathToObj = new Map();
  let o = JSON.parse(json);
  
  let traverse = (parent, field) => {
    let obj = parent;
    let path = '#REF:$';

    if (field !== undefined) {
      obj = parent[field];
      path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
    }

    objToPath.set(obj, path);
    pathToObj.set(path, obj);
    
    let ref = pathToObj.get(obj);
    if (ref) parent[field] = ref;

    for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
  }
  
  traverse(o);
  return o;
}



// ------------
// TEST
// ------------

let s = `{
    "o1": {
        "o2": {
            "a1": 1,
            "a2": 2,
            "a3": [
                1,
                2,
                {
                    "b1": 3,
                    "b2": "4",
                    "b3": "#REF:$.o1.o2"
                }
            ]
        }
    },
    "b": "#REF:$.o1.o2.a3[2]",
    "a": "#REF:$.o1.o2"
}`;

console.log('Open Chrome console to see nested fields:');
let obj = parseRefJSON(s);

console.log(obj);

于 2020-05-12T10:48:53.553 に答える
5

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でのみ使用できます

これらのいずれかがあなたのニーズを満たすはずです。

于 2013-07-22T04:46:50.030 に答える
4

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

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

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

于 2017-03-30T20:20:53.277 に答える
-13

循環参照を削除するために、以下を使用しました。

JS.dropClasses = function(o) {

    for (var p in o) {
        if (o[p] instanceof jQuery || o[p] instanceof HTMLElement) {
            o[p] = null;
        }    
        else if (typeof o[p] == 'object' )
            JS.dropClasses(o[p]);
    }
};

JSON.stringify(JS.dropClasses(e));
于 2014-02-28T19:41:46.867 に答える