web-gelistirme-sc.com

JSON.stringify, TypeError'dan kaçının: Dairesel yapıyı JSON'a dönüştürme

JSON'a dönüştürmek ve göndermek istediğim büyük bir nesne var. Ancak dairesel bir yapıya sahiptir. Var olan dairesel referansları atmak ve dizilebilecek olanları göndermek istiyorum. Bunu nasıl yaparım?

Teşekkürler.

var obj = {
  a: "foo",
  b: obj
}

Nesneyi şöyle tanımlamak istiyorum:

{"a":"foo"}
464
Harry

Özel bir replacer ile JSON.stringify kullanın. Örneğin:

// Demo: Circular reference
var o = {};
o.o = o;

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(o, function(key, value) {
    if (typeof value === 'object' && value !== null) {
        if (cache.indexOf(value) !== -1) {
            // Duplicate reference found
            try {
                // If this value does not reference a parent it can be deduped
                return JSON.parse(JSON.stringify(value));
            } catch (error) {
                // discard key if value cannot be deduped
                return;
            }
        }
        // Store value in our collection
        cache.Push(value);
    }
    return value;
});
cache = null; // Enable garbage collection

Bu örnekteki replacer% 100 doğru değil ("yinelenen" tanımınıza bağlı olarak). Aşağıdaki durumda, bir değer atılır:

var a = {b:1}
var o = {};
o.one = a;
o.two = a;
// one and two point to the same object, but two is discarded:
JSON.stringify(o, ...);

Fakat konsept geçerli: Özel bir replacer kullanın ve ayrıştırılan nesne değerlerini takip edin.

456
Rob W

Node.js'de util.inspect (object) öğesini kullanabilirsiniz. Dairesel bağlantıları otomatik olarak "[Dairesel]" ile değiştirir.


Yerleşik olmasına rağmen (kurulum gerekmez), içe aktarmanız gerekir

import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or 
var util = require('util')
console.log(util.inspect(myObject))

Ayrıca, incelemek için seçenekler nesnesini iletebileceğinizi unutmayın. (Yukarıdaki bağlantıya bakın)

inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])



Lütfen, okuyup aşağıdaki yorumculara övgülerinizi verin ...

486
Erel Segal-Halevi

Neden hiç kimseyi göndermedi acaba MDN sayfasından uygun çözüm henüz ...

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

JSON.stringify(circularReference, getCircularReplacer());

Görülen değerlerbir sette, dizide değil (replacer,her elemandaolarak adlandırılır) ve orada saklanmalıdır. dairesel bir referansa yol açan zincirde JSON.stringifyher elementdenemeye gerek yoktur.

Kabul edilen cevapta olduğu gibi, bu çözüm sadece dairesel olanları değil,tüm yinelenen değerlerikaldırır. Fakat en azından üstel karmaşıklığa sahip değil.

91
Artur Klesun

sadece yap 

npm i --save circular-json

sonra js dosyanızda

const JSON = require('circular-json');
...
const json = JSON.stringify(obj);

Ayrıca yapabilir 

const CircularJSON = require('circular-json');

https://github.com/WebReflection/circular-json

NOT: Bu paketle ilgim yok. Ama bunun için kullanıyorum.

55
user1541685

Trindaz'in çözümünü gerçekten çok beğendim - daha ayrıntılı, ancak bazı böcekleri vardı. Onları da sevenler için düzelttim.

Ayrıca, önbellek nesnelerime bir uzunluk sınırı ekledim. 

Yazdırdığım nesne gerçekten büyükse - sonsuz büyük demek istiyorum - algoritmamı sınırlandırmak istiyorum. 

JSON.stringifyOnce = function(obj, replacer, indent){
    var printedObjects = [];
    var printedObjectKeys = [];

    function printOnceReplacer(key, value){
        if ( printedObjects.length > 2000){ // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
        return 'object too long';
        }
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if ( key == ''){ //root element
             printedObjects.Push(obj);
            printedObjectKeys.Push("root");
             return value;
        }

        else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
            if ( printedObjectKeys[printedObjIndex] == "root"){
                return "(pointer to root)";
            }else{
                return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase()  : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
            }
        }else{

            var qualifiedKey = key || "(empty key)";
            printedObjects.Push(value);
            printedObjectKeys.Push(qualifiedKey);
            if(replacer){
                return replacer(key, value);
            }else{
                return value;
            }
        }
    }
    return JSON.stringify(obj, printOnceReplacer, indent);
};
46
guy mograbi

Ayrıca, Douglas Crockford tarafından uygulanan bir JSON.decycle yöntemi olduğuna dikkat edin. Bkz onun cycle.js . Bu, neredeyse tüm standart yapıları dizginlemenizi sağlar:

var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.

Orijinal nesneyi retrocycle yöntemi ile de yeniden oluşturabilirsiniz. Bu yüzden nesnelerin dizgelerini çıkarmak için bunları silmeniz gerekmez.

Ancak bu DOM Düğümleri için çalışmaz (gerçek hayat kullanım durumlarında tipik döngü nedenidir). Örneğin, bu atıyor:

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));

Bu sorunu çözmek için bir çatal yaptım (bkz. cycle.js fork ). Bu iyi çalışması gerekir:

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));

Çatalımda JSON.decycle(variable) öğesinin orijinali gibi çalıştığını ve variable DOM düğümleri/öğeleri içerdiğinde bir istisna atacağını unutmayın.

JSON.decycle(variable, true) kullandığınızda, sonucun geri dönüşümlü olmayacağını kabul edersiniz (retrocycle, DOM düğümlerini yeniden oluşturmaz). DOM elemanları bir dereceye kadar tanımlanabilir olmalıdır. Örneğin, bir div öğesinin bir kimliği varsa, o zaman "div#id-of-the-element" dizesiyle değiştirilecektir.

35
Nux

@ RobW'ın cevabı doğru, ancak bu daha performanslı! Bir hashmap/set kullandığı için:

const customStringify = function (v) {
  const cache = new Set();
  return JSON.stringify(v, function (key, value) {
    if (typeof value === 'object' && value !== null) {
      if (cache.has(value)) {
        // Circular reference found
        try {
          // If this value does not reference a parent it can be deduped
         return JSON.parse(JSON.stringify(value));
        }
        catch (err) {
          // discard key if value cannot be deduped
         return;
        }
      }
      // Store value in our set
      cache.add(value);
    }
    return value;
  });
};
30
Alexander Mills

Kontrol etmenizi tavsiye ederim json-stringify-safe @ isaacs'dan-- NPM'de kullanılıyor.

BTW- Node.js kullanmıyorsanız --- 4-27 satırlarını kopyalayıp yapıştırabilirsiniz kaynak kodun ilgili kısmı .

Yüklemek:

$ npm install json-stringify-safe --save

Kullanmak:

// Require the thing
var stringify = require('json-stringify-safe');

// Take some nasty circular object
var theBigNasty = {
  a: "foo",
  b: theBigNasty
};

// Then clean it up a little bit
var sanitized = JSON.parse(stringify(theBigNasty));

Bu verim:

{
  a: 'foo',
  b: '[Circular]'
}

Vanilla JSON.stringify işlevinde olduğu gibi, @Rob W'de belirtildiği gibi, sanitasyon işlemini bir "replacer" işlevini ikinci argüman olarak stringify() öğesine geçirerek de özelleştirebilirsiniz. Bunun nasıl yapılacağına dair basit bir örneğe ihtiyaç duyuyorsanız, hataları, regexps'i ve işlevleri insan tarafından okunabilen dizgelere zorlayan özel bir replacer yazdım burada .

22
mikermcneil

Tüm dairesel referansların anahtarlarını bildiğiniz zaman bilmiyorum bildiğiniz zaman bu soruna bir çözüm arayan Google çalışanları, dairesel referansları ekarte etmek için JSON.stringify işlevinin etrafında bir sarıcı kullanabilirsiniz. Örnek bir betiğe bakın https://Gist.github.com/4653128 .

Çözüm, bir dizideki daha önce basılmış nesnelere bir referans tutmak ve bir değer döndürmeden önce bir replacer işlevinde kontrol etmek için esasen aşağı kaymaktadır. Dairesel referansları dışlamaktan daha da sınırlayıcıdır, çünkü aynı zamanda bir nesneyi iki kez basmayı da yasaklar, bunlardan biri de çevresel referanslardan kaçınmaktır.

Örnek sarmalayıcı:

function stringifyOnce(obj, replacer, indent){
    var printedObjects = [];
    var printedObjectKeys = [];

    function printOnceReplacer(key, value){
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if(printedObjIndex && typeof(value)=="object"){
            return "(see " + value.constructor.name.toLowerCase() + " with key " + printedObjectKeys[printedObjIndex] + ")";
        }else{
            var qualifiedKey = key || "(empty key)";
            printedObjects.Push(value);
            printedObjectKeys.Push(qualifiedKey);
            if(replacer){
                return replacer(key, value);
            }else{
                return value;
            }
        }
    }
    return JSON.stringify(obj, printOnceReplacer, indent);
}
12
Trindaz
var a={b:"b"};
a.a=a;
JSON.stringify(preventCircularJson(a));

değerlendirir:

"{"b":"b","a":"CIRCULAR_REFERENCE_REMOVED"}"

fonksiyonu ile:

/**
 * Traverses a javascript object, and deletes all circular values
 * @param source object to remove circular references from
 * @param censoredMessage optional: what to put instead of censored values
 * @param censorTheseItems should be kept null, used in recursion
 * @returns {undefined}
 */
function preventCircularJson(source, censoredMessage, censorTheseItems) {
    //init recursive value if this is the first call
    censorTheseItems = censorTheseItems || [source];
    //default if none is specified
    censoredMessage = censoredMessage || "CIRCULAR_REFERENCE_REMOVED";
    //values that have allready apeared will be placed here:
    var recursiveItems = {};
    //initaite a censored clone to return back
    var ret = {};
    //traverse the object:
    for (var key in source) {
        var value = source[key]
        if (typeof value == "object") {
            //re-examine all complex children again later:
            recursiveItems[key] = value;
        } else {
            //simple values copied as is
            ret[key] = value;
        }
    }
    //create list of values to censor:
    var censorChildItems = [];
    for (var key in recursiveItems) {
        var value = source[key];
        //all complex child objects should not apear again in children:
        censorChildItems.Push(value);
    }
    //censor all circular values
    for (var key in recursiveItems) {
        var value = source[key];
        var censored = false;
        censorTheseItems.forEach(function (item) {
            if (item === value) {
                censored = true;
            }
        });
        if (censored) {
            //change circular values to this
            value = censoredMessage;
        } else {
            //recursion:
            value = preventCircularJson(value, censoredMessage, censorChildItems.concat(censorTheseItems));
        }
        ret[key] = value

    }

    return ret;
}
4
eshalev

JSON.stringify yöntemini bir replacer ile kullanın. Daha fazla bilgi için bu belgeyi okuyun. http://msdn.Microsoft.com/en-us/library/cc836459%28v=vs.94%29.aspx

var obj = {
  a: "foo",
  b: obj
}

var replacement = {"b":undefined};

alert(JSON.stringify(obj,replacement));

Değişim dizisini döngüsel referanslarla doldurmanın bir yolunu bulun. Bir özelliğin 'object' (referans) türünde olup olmadığını ve dairesel referansı doğrulamak için tam bir eşitlik kontrolünün (===) olup olmadığını bulmak için typeof yöntemini kullanabilirsiniz.

4
TWickz

Bunun eski bir soru olduğunu biliyorum, ancak smart-circular adında oluşturduğum bir NPM paketi önermek istiyorum. Big ve deep nesneleri kullanıyorsanız özellikle yararlıdır. 

Bazı özellikler:

  • Dairesel referansları veya nesnenin içindeki basit tekrarlanan yapıları, ilk oluşumuna yol açan yolla değiştirmek (sadece [circular] dizgisine değil);

  • Geniş kapsamlı bir aramada döngüselliği arayarak, paket bu yolun mümkün olduğu kadar küçük olmasını sağlar; bu, yolların rahatsız edici derecede uzun ve takip etmesi zor olan çok büyük ve derin nesnelerle uğraşırken önemlidir. JSON.stringify bir DFS yapar);

  • Nesnenin daha az önemli kısımlarını basitleştirmek veya yok saymak için kullanışlı kişiselleştirilmiş değiştirmelere izin verir;

  • Son olarak, yollar tam olarak hata ayıklama yapmanıza yardımcı olabilecek, başvurulan alana erişmek için gerekli şekilde yazılır.

3
Danilo Augusto

Bu sorunu şöyle çözüyorum:

var util = require('util');

// Our circular object
var obj = {foo: {bar: null}, a:{a:{a:{a:{a:{a:{a:{hi: 'Yo!'}}}}}}}};
obj.foo.bar = obj;

// Generate almost valid JS object definition code (typeof string)
var str = util.inspect(b, {depth: null});

// Fix code to the valid state (in this example it is not required, but my object was huge and complex, and I needed this for my case)
str = str
    .replace(/<Buffer[ \w\.]+>/ig, '"buffer"')
    .replace(/\[Function]/ig, 'function(){}')
    .replace(/\[Circular]/ig, '"Circular"')
    .replace(/\{ \[Function: ([\w]+)]/ig, '{ $1: function $1 () {},')
    .replace(/\[Function: ([\w]+)]/ig, 'function $1(){}')
    .replace(/(\w+): ([\w :]+GMT\+[\w \(\)]+),/ig, '$1: new Date("$2"),')
    .replace(/(\S+): ,/ig, '$1: null,');

// Create function to eval stringifyed code
var foo = new Function('return ' + str + ';');

// And have fun
console.log(JSON.stringify(foo(), null, 4));
1
MiF

JSON.stringify () ayrıca 'nin ikinci argümanı, verilerinizde karşılaştığı her nesneden korunması gereken bir anahtar ad dizisi belirlemenizi sağlar. Bu, tüm kullanım durumlarında çalışmayabilir, ancak çok daha basit bir çözümdür.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

var obj = {
    a: "foo",
    b: this
}

var json = JSON.stringify(obj, ['a']);
console.log(json);
// {"a":"foo"}

Not: Garip bir şekilde, OP'den gelen nesne tanımı, en son Chrome'da veya Firefox'ta dairesel bir referans hatası atmıyor. Bu cevaptaki tanım, did hata atmak üzere değiştirildi.


1
Aaron Cicali

Diğer cevaplara dayanarak aşağıdaki kodla bitirdim. Dairesel referanslarla, özel yapımcılara sahip nesnelerle oldukça iyi çalışır.

Serileştirilecek olan nesneden, 

  • Nesnenin üzerinden geçerken karşılaştığınız tüm nesneleri önbelleğe alın ve her birine benzersiz bir hashID atayın (otomatik artan bir sayı da çalışır)
  • Dairesel bir referans bulunduğunda, o alandaki yeni nesneyi dairesel olarak işaretleyin ve orijinal nesnenin hashID'sini bir nitelik olarak saklayın.

Github Bağlantı - DecycledJSON

DJSHelper = {};
DJSHelper.Cache = [];
DJSHelper.currentHashID = 0;
DJSHelper.ReviveCache = [];

// DOES NOT SERIALIZE FUNCTION
function DJSNode(name, object, isRoot){
    this.name = name;
    // [ATTRIBUTES] contains the primitive fields of the Node
    this.attributes = {};

    // [CHILDREN] contains the Object/Typed fields of the Node
    // All [CHILDREN] must be of type [DJSNode]
    this.children = []; //Array of DJSNodes only

    // If [IS-ROOT] is true reset the Cache and currentHashId
    // before encoding
    isRoot = typeof isRoot === 'undefined'? true:isRoot;
    this.isRoot = isRoot;
    if(isRoot){
        DJSHelper.Cache = [];
        DJSHelper.currentHashID = 0;

        // CACHE THE ROOT
        object.hashID = DJSHelper.currentHashID++;
        DJSHelper.Cache.Push(object);
    }

    for(var a in object){
        if(object.hasOwnProperty(a)){
            var val = object[a];

            if (typeof val === 'object') {
                // IF OBJECT OR NULL REF.

                /***************************************************************************/
                // DO NOT REMOVE THE [FALSE] AS THAT WOULD RESET THE [DJSHELPER.CACHE]
                // AND THE RESULT WOULD BE STACK OVERFLOW
                /***************************************************************************/
                if(val !== null) {
                    if (DJSHelper.Cache.indexOf(val) === -1) {
                        // VAL NOT IN CACHE
                        // ADD THE VAL TO CACHE FIRST -> BEFORE DOING RECURSION
                        val.hashID = DJSHelper.currentHashID++;
                        //console.log("Assigned", val.hashID, "to", a);
                        DJSHelper.Cache.Push(val);

                        if (!(val instanceof Array)) {
                            // VAL NOT AN [ARRAY]
                            try {
                                this.children.Push(new DJSNode(a, val, false));
                            } catch (err) {
                                console.log(err.message, a);
                                throw err;
                            }
                        } else {
                            // VAL IS AN [ARRAY]
                            var node = new DJSNode(a, {
                                array: true,
                                hashID: val.hashID // HashID of array
                            }, false);
                            val.forEach(function (elem, index) {
                                node.children.Push(new DJSNode("elem", {val: elem}, false));
                            });
                            this.children.Push(node);
                        }
                    } else {
                        // VAL IN CACHE
                        // ADD A CYCLIC NODE WITH HASH-ID
                        this.children.Push(new DJSNode(a, {
                            cyclic: true,
                            hashID: val.hashID
                        }, false));
                    }
                }else{
                    // PUT NULL AS AN ATTRIBUTE
                    this.attributes[a] = 'null';
                }
            } else if (typeof val !== 'function') {
                // MUST BE A PRIMITIVE
                // ADD IT AS AN ATTRIBUTE
                this.attributes[a] = val;
            }
        }
    }

    if(isRoot){
        DJSHelper.Cache = null;
    }
    this.constructorName = object.constructor.name;
}
DJSNode.Revive = function (xmlNode, isRoot) {
    // Default value of [isRoot] is True
    isRoot = typeof isRoot === 'undefined'?true: isRoot;
    var root;
    if(isRoot){
        DJSHelper.ReviveCache = []; //Garbage Collect
    }
    if(window[xmlNode.constructorName].toString().indexOf('[native code]') > -1 ) {
        // yep, native in the browser
        if(xmlNode.constructorName == 'Object'){
            root = {};
        }else{
            return null;
        }
    }else {
        eval('root = new ' + xmlNode.constructorName + "()");
    }

    //CACHE ROOT INTO REVIVE-CACHE
    DJSHelper.ReviveCache[xmlNode.attributes.hashID] = root;

    for(var k in xmlNode.attributes){
        // PRIMITIVE OR NULL REF FIELDS
        if(xmlNode.attributes.hasOwnProperty(k)) {
            var a = xmlNode.attributes[k];
            if(a == 'null'){
                root[k] = null;
            }else {
                root[k] = a;
            }
        }
    }

    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.array){
            // ITS AN [ARRAY]
            root[value.name] = [];
            value.children.forEach(function (elem) {
                root[value.name].Push(elem.attributes.val);
            });
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }else if(!value.attributes.cyclic){
            // ITS AN [OBJECT]
            root[value.name] = DJSNode.Revive(value, false);
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }
    });

    // [SEPARATE ITERATION] TO MAKE SURE ALL POSSIBLE
    // [CYCLIC] REFERENCES ARE CACHED PROPERLY
    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.cyclic){
            // ITS AND [CYCLIC] REFERENCE
            root[value.name] = DJSHelper.ReviveCache[value.attributes.hashID];
        }
    });

    if(isRoot){
        DJSHelper.ReviveCache = null; //Garbage Collect
    }
    return root;
};

DecycledJSON = {};
DecycledJSON.stringify = function (obj) {
    return JSON.stringify(new DJSNode("root", obj));
};
DecycledJSON.parse = function (json, replacerObject) {
    // use the replacerObject to get the null values
    return DJSNode.Revive(JSON.parse(json));
};
DJS = DecycledJSON;

Örnek Kullanım 1:

var obj = {
    id:201,
    box: {
        owner: null,
        key: 'storm'
    },
    lines:[
        'item1',
        23
    ]
};

console.log(obj); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonObj = DJS.stringify(obj);
console.log(DJS.parse(jsonObj));

Örnek Kullanım 2:

// PERSON OBJECT

function Person() {
    this.name = null;
    this.child = null;
    this.dad = null;
    this.mom = null;
}
var Dad = new Person();
Dad.name = 'John';
var Mom = new Person();
Mom.name = 'Sarah';
var Child = new Person();
Child.name = 'Kiddo';

Dad.child = Mom.child = Child;
Child.dad = Dad;
Child.mom = Mom;

console.log(Child); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonChild = DJS.stringify(Child);
console.log(DJS.parse(jsonChild));
0
bytestorm

bu konuyu bu tür nesnelerle çözmek için başka bir çözüm bu kütüphaneyi kullanmaktır.

https://github.com/ericmuyser/stringy

basit ve birkaç basit adımda bunu çözebilirsiniz.

0
Ehsan Aghaei

Bu, yeterince cevaplanmış olmasına rağmen, delete işlecini kullanarak dize işleminden önce söz konusu özelliği açıkça silebilirsiniz.

delete obj.b; 
const jsonObject = JSON.stringify(obj);

operatörü sil

bu, dairesel referansları kaldırmak için karmaşık mantık oluşturma veya sürdürme ihtiyacını ortadan kaldıracaktır.

0
lachlan.p.jordan

JSON'un çalışma şeklini geçersiz kılma yanıtını güncellemek için (muhtemelen önerilmez, ancak çok basit), circular-json kullanmayın (kullanımdan kaldırılmıştır). Bunun yerine, halefi düzleştirin:

https://www.npmjs.com/package/flatted

Yukarıdaki eski cevaptan ödünç alındı ​​@ user1541685, ancak yenisiyle değiştirildi:

npm i --save flatted

sonra js dosyanızda

const JSON = require('flatted');

...

const json = JSON.stringify(obj);

Ayrıca yapabilir

const CircularJSON = require('flatted');

0
dylanh724
function myStringify(obj, maxDeepLevel = 2) {
  if (obj === null) {
    return 'null';
  }
  if (obj === undefined) {
    return 'undefined';
  }
  if (maxDeepLevel < 0 || typeof obj !== 'object') {
    return obj.toString();
  }
  return Object
    .entries(obj)
    .map(x => x[0] + ': ' + myStringify(x[1], maxDeepLevel - 1))
    .join('\r\n');
}
0
Sergey Gurin

Bu sorunun eski olduğunu ve çok büyük cevapları olduğunu biliyorum, ancak yeni cevabı nedeniyle bu cevabı gönderiyorum (es5 +) 

Object.defineProperties(JSON, {
  refStringify: {
    value: function(obj) {

      let objMap = new Map();
      let stringified = JSON.stringify(obj,
        function(key, value) {

          // only for objects
          if (typeof value == 'object') {
            // If has the value then return a reference to it
            if (objMap.has(value))
              return objMap.get(value);

            objMap.set(value, `ref${objMap.size + 1}`);
          }
          return value;
        });
      return stringified;
    }
  },
  refParse: {
    value: function(str) {

      let parsed = JSON.parse(str);
      let objMap = _createObjectMap(parsed);
      objMap.forEach((value, key) => _replaceKeyWithObject(value, key));
      return parsed;
    }
  },
});

// *************************** Example
let a = {
  b: 32,
  c: {
    get a() {
        return a;
      },
      get c() {
        return a.c;
      }
  }
};
let stringified = JSON.refStringify(a);
let parsed = JSON.refParse(stringified, 2);
console.log(parsed, JSON.refStringify(parsed));
// *************************** /Example

// *************************** Helper
function _createObjectMap(obj) {

  let objMap = new Map();
  JSON.stringify(obj, (key, value) => {
    if (typeof value == 'object') {
      if (objMap.has(value))
        return objMap.get(value);
      objMap.set(value, `ref${objMap.size + 1}`);

    }
    return value;
  });
  return objMap;
}

function _replaceKeyWithObject(key, obj, replaceWithObject = obj) {

  Object.keys(obj).forEach(k => {

    let val = obj[k];
    if (val == key)
      return (obj[k] = replaceWithObject);
    if (typeof val == 'object' && val != replaceWithObject)
      _replaceKeyWithObject(key, val, replaceWithObject);
  });
}

0
Morteza Tourani

Github'da circular-json kütüphanesi buldum ve sorunum için iyi çalıştı.

Yararlı bulduğum bazı iyi özellikler:

  • Çoklu platform kullanımını destekler, ancak şimdiye kadar yalnızca node.js ile test ettim.
  • API aynıdır, tek yapmanız gereken onu bir JSON yedeği olarak kullanmak ve kullanmaktır.
  • Kendi ayrıştırma yöntemine sahiptir, böylece 'dairesel' serileştirilmiş verileri nesneye geri dönüştürebilirsiniz.
0
JacopKane

Eğer 

console.log(JSON.stringify(object));

bir sonuç 

TypeError: döngüsel nesne değeri

O zaman şöyle yazdırmak isteyebilirsiniz:

var output = '';
for (property in object) {
  output += property + ': ' + object[property]+'; ';
}
console.log(output);
0
Thorsten Niehues

Bunu dene:

var obj = {
    a: "foo",
    b: obj
};

var circular_replacer = (value) => {
    var seen = [];
    if (value != null && typeof value == "object") {
        if (seen.indexOf(value) >= 0) return;
        seen.Push(value);
    }
    return value;
};

obj = circular_replacer(obj);
0
IamMHussain