循環オブジェクト値を含むオブジェクトのシリアライズ


151

他のノードへの参照である子ノードを含むオブジェクト(解析ツリー)があります。

を使用してこのオブジェクトをシリアル化したいのJSON.stringify()ですが、

TypeError:循環オブジェクト値

私が言及した構成のために。

どうすればこれを回避できますか?他のノードへのこれらの参照がシリアル化されたオブジェクトで表現されているかどうかは、私には関係ありません。

一方、これらのプロパティを作成中にオブジェクトから削除するのは面倒で、パーサー(水仙)を変更したくありません。


1
コードがなければ、私たちはあなたを助けることはできません。オブジェクトおよび/またはJSON出力の関連ビットを、シリアル化に使用するJSとともに投稿してください。
Bojangles

1
内部参照であるプロパティにプレフィックスを追加できますか?
wheresrhys

@Loic cycle.js多くの場合に最も適切なソリューションであるため、Douglas Crockford's をここでの回答として使用することは価値があります。あなたがその回答を最初に参照するので(以下のコメントで)、あなたはその回答を投稿するのが適切なようです。自分で回答として投稿したくない場合は、最終的には投稿します。
Jeremy Banks


1
私は、JSONがより賢く、またはこれを解決するより簡単な方法であることを願っています。ソリューションは、単純な(!)デバッグ目的のimoにはあまりにも面倒です。
BluE

回答:


220

の2番目のパラメーターでstringifyあるreplacer関数を使用して、既にシリアル化されたオブジェクトを除外します。

var seen = [];

JSON.stringify(obj, function(key, val) {
   if (val != null && typeof val == "object") {
        if (seen.indexOf(val) >= 0) {
            return;
        }
        seen.push(val);
    }
    return val;
});

http://jsfiddle.net/mH6cJ/38/

他のコメントで正しく指摘されているように、このコードは「再帰的な」オブジェクトだけでなく、「目に見える」すべてのオブジェクトを削除します。

たとえば、次の場合:

a = {x:1};
obj = [a, a];

結果は正しくありません。構造が次のようになっている場合は、再帰参照をnullに置き換えるだけのCrockfordのdecycleまたはこの(より単純な)関数を使用できます。

function decycle(obj, stack = []) {
    if (!obj || typeof obj !== 'object')
        return obj;
    
    if (stack.includes(obj))
        return null;

    let s = stack.concat([obj]);

    return Array.isArray(obj)
        ? obj.map(x => decycle(x, s))
        : Object.fromEntries(
            Object.entries(obj)
                .map(([k, v]) => [k, decycle(v, s)]));
}

//

let a = {b: [1, 2, 3]}
a.b.push(a);

console.log(JSON.stringify(decycle(a)))


3
いいね!よろしくお願いします。Douglas Crockford(github.com/douglascrockford/JSON-js/blob/master/cycle.js)によって作成されたソリューションを見つけましたが、それに伴うライセンスがわからないので、あなたが説明する簡単なソリューションは完璧です!
ロイックデュロス

3
@LoicDurosライセンスは「パブリックドメイン」です。つまり、あなたはそれであなたがやりたいことを何でもすることができます。
Ates Goral 2012年

1
このコードは、循環ループを生成します。使用に注意してください。アプリが非常に潜在的にクラッシュします。正しいセミコロンが必要であり、イベントオブジェクトでは使用できません!
Ol Sen

3
これにより、循環参照だけでなく、複数回出現するものもすべて削除されます。既にシリアル化されているオブジェクトが新しいオブジェクトの "親"でない限り、削除しないでください
Gio

1
いい答えだ!これを少し変更して、関数を再帰関数に変更しました。これにより、子オブジェクトは親オブジェクトが複製される方法で複製されます。
HoldOffHunger

2

私はGitHub Gistを作成しました。これは、循環構造を検出し、それらをデコードおよびエンコードすることもできます。https//gist.github.com/Hoff97/9842228

変換するには、JSONE.stringify / JSONE.parseを使用します。また、関数をデコードおよびエンコードします。これを無効にしたい場合は、32-48行と61-85行を削除してください。

var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);

ここでフィドルの例を見つけることができます:

http://jsfiddle.net/hoff97/7UYd4/


2

これは一種の代替解ですが、多くの人々がここに来るのはそれらの循環オブジェクトをデバッグすることであり、たくさんのコードを引き込まずにそれを行うには本当に良い方法がないので、ここに行きます。

よく知られているようではありません一つの特徴JSON.stringify()ですconsole.table()。単にを呼び出すconsole.table(whatever);と、変数が表形式でコンソールに記録され、変数の内容を簡単に閲覧できるようになります。


1

多くのセーバーとそれはサイクルオブジェクトがあった場所を示しています。

<script>
var jsonify=function(o){
    var seen=[];
    var jso=JSON.stringify(o, function(k,v){
        if (typeof v =='object') {
            if ( !seen.indexOf(v) ) { return '__cycle__'; }
            seen.push(v);
        } return v;
    });
    return jso;
};
var obj={
    g:{
        d:[2,5],
        j:2
    },
    e:10
};
obj.someloopshere = [
    obj.g,
    obj,
    { a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>

作り出す

jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

それでも誰かが持つオブジェクトビルドする場合は、このコードに問題があるobj.b=this'、誰かが間違って与えられた範囲で作られた非常に長いCALCSを防ぐ方法を知っている場合はthisこちらを参照してくださいいいだろう
オルセン

2
これはseen.indexOf(v) != -1

1

Stringのようにserializename属性に保存すると、循環オブジェクトをシリアル化してクラスを復元​​できるgithubプロジェクトも作成します

var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal(  b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal(  retCaseDep.b, 25 );
assert.equal(  retCaseDep.enfant.papa, retCaseDep );

https://github.com/bormat/serializeStringifyParseCyclicObject

編集:スクリプトをNPM https://github.com/bormat/borto_circular_serializeに変換し、関数名をフランス語から英語に変更しました。


この例は要旨に適合していません。Gistにエラーがあります。
Ernst Ernst

いい考えですが、それを準備してください:-) npmで配布するようにすると、おそらくそのためのタイピングさえ開発することになるでしょう。
peterh-モニカを2017年

1

以下は、循環参照のあるデータ構造の例です。 toolshedCY

function makeToolshed(){
    var nut = {name: 'nut'}, bolt = {name: 'bolt'};
    nut.needs = bolt; bolt.needs = nut;
    return { nut: nut, bolt: bolt };
}

あなたがしたい場合はKEEP循環参照を(あなたがデシリアライズ時に代わりにそれらを「ニューキング」の、それらを復元)、あなたは私がここで比較します2つの選択肢を持っています。1つ目はDouglas Crockfordのcycle.jsで、2つ目は私のシベリアですパッケージです。どちらも最初にオブジェクトを「デサイクル」することで機能します。つまり、「同じ情報を含む」別のオブジェクト(循環参照なし)を構築します。

クロックフォード氏が最初に行く:

JSON.decycle(makeToolshed())

JSON_decycleMakeToolshed

ご覧のように、JSONの入れ子構造は保持されていますが、特別な$refプロパティを持つオブジェクトという新しいものがあります。それがどのように機能するか見てみましょう。

root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]

ドル記号はルートを表します。.bolt持っている$refことを教えてくれる.bolt「すでに見」オブジェクトであり、そして、最初の参照場所は、特別なプロパティの値が(ここでは、文字列$ [「ナット」] [「ニーズ」])を教えてくれる===以上。上記の2番目$refと2番目も同様です===

適切なディープイクイティテストを使用してみましょう(つまりdeepGraphEqual、受け入れられた答えからAnders Kaseorgの関数この質問)を使用して、クローンが機能するかどうかを確認します。

root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true

今、シベリア:

JSON.Siberia.forestify(makeToolshed())

JSON_Siberia_forestify_makeToolshed

シベリアはネストされた構造ではなく、「クラシック」JSONを模倣しようとしません。オブジェクトグラフは「フラット」な方法で記述されます。オブジェクトグラフの各ノードはフラットツリー(整数のみの値を持つプレーンなキーと値のペアのリスト)に変わります。これは、.forest.インデックス0のエントリです。ルートオブジェクトを見つけ、より高いインデックスで他のノードを見つけます。オブジェクトグラフ、および(フォレストのツリーのいくつかのキーの)負の値は、atoms配列(types配列を介して入力されますが、ここでは入力の詳細を省略します)を指します。すべての末端ノードはアトムテーブルにあり、すべての非末端ノードはフォレストテーブルにあり、オブジェクトグラフのノード数、つまりをすぐに確認できますforest.length。それが機能するかどうかをテストしましょう:

root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true

比較

後でセクションを追加します。


0
function stringifyObject ( obj ) {
  if ( _.isArray( obj ) || !_.isObject( obj ) ) {
    return obj.toString()
  }
  var seen = [];
  return JSON.stringify(
    obj,
    function( key, val ) {
      if (val != null && typeof val == "object") {
        if ( seen.indexOf( val ) >= 0 )
          return
          seen.push( val )
          }
      return val
    }
  );
}

前提条件がありませんでした。それ以外の場合、配列オブジェクトの整数値は切り捨てられます。つまり、[[08.11.2014 12:30:13、1095]] 1095は095に減少します。


RefrenceErrorの取得:変数が見つかりません:_
amit pandya

コードを修正してください。
Anastasios Moraitis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.