it-swarm.com.de

JSON.stringify, vermeidet TypeError: Konvertieren einer Kreisstruktur in JSON

Ich habe ein großes Objekt, das ich in JSON konvertieren und senden möchte. Es hat jedoch eine kreisförmige Struktur. Ich möchte die vorhandenen Zirkelverweise werfen und senden, was auch stringifiziert werden kann. Wie mache ich das?

Vielen Dank.

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

Ich möchte obj in ein String konfizieren

{"a":"foo"}
464
Harry

Verwenden Sie JSON.stringify mit einem benutzerdefinierten Ersetzungsprogramm. Zum Beispiel:

// 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

Der Ersetzer in diesem Beispiel ist nicht 100% korrekt (abhängig von Ihrer Definition von "Duplikat"). In dem folgenden Fall wird ein Wert verworfen:

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, ...);

Das Konzept steht jedoch: Verwenden Sie einen benutzerdefinierten Ersetzer, und verfolgen Sie die geparsten Objektwerte.

456
Rob W

In Node.js können Sie util.inspect (object) verwenden. Es werden kreisförmige Links automatisch durch "[Circular]" ersetzt.


Obwohl es sich um ein eingebautes (keine Installation ist erforderlich) handelt, müssen Sie es importieren

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))

Beachten Sie auch, dass Sie ein Optionsobjekt zur Überprüfung von übergeben können (siehe Link oben).

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



Bitte, lesen Sie und geben Sie den Kommentaren unten ein Lob ...

486

mach einfach 

npm i --save circular-json

dann in deiner js-datei

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

Du könntest es auch tun 

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

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

HINWEIS: Ich habe nichts mit diesem Paket zu tun. Aber ich benutze es dafür.

55
user1541685

Ich mochte die Lösung von Trindaz wirklich - mehr wortreich, es gab jedoch einige Fehler. Ich habe sie für jeden, der es mag, repariert.

Außerdem habe ich eine Längenbegrenzung für meine Cache-Objekte hinzugefügt. 

Wenn das Objekt, das ich drucke, wirklich groß ist - ich meine, unendlich groß -, möchte ich meinen Algorithmus einschränken. 

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

Beachten Sie, dass es auch eine von Douglas Crockford implementierte JSON.decycle-Methode gibt. Siehe his cycle.js . Dadurch können Sie nahezu jede Standardstruktur festlegen:

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

Sie können das Originalobjekt auch mit der retrocycle-Methode neu erstellen. Sie müssen also keine Zyklen aus Objekten entfernen, um sie zu straffen.

Dies wird jedoch nicht für DOM-Knoten funktionieren (typische Ursachen für Zyklen in realen Anwendungsfällen). Zum Beispiel wird dies werfen:

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

Ich habe eine Gabel gemacht, um dieses Problem zu lösen (siehe meine cycle.js fork ). Das sollte gut funktionieren:

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

Beachten Sie, dass JSON.decycle(variable) in meiner Abzweigung wie im Original funktioniert und eine Ausnahme auslöst, wenn die variable DOM-Knoten/-Elemente enthält.

Wenn Sie JSON.decycle(variable, true) verwenden, akzeptieren Sie die Tatsache, dass das Ergebnis nicht umkehrbar ist (Retrocycle erstellt keine DOM-Knoten neu). DOM-Elemente sollten jedoch in gewissem Umfang erkennbar sein. Wenn ein div-Element beispielsweise eine ID hat, wird diese durch eine Zeichenfolge "div#id-of-the-element" ersetzt.

35
Nux

@ RobWs Antwort ist richtig, aber das ist performanter! Weil es ein Hashmap/Set verwendet:

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

Ich frage mich, warum niemand die richtige Lösung von der MDN-Seite gepostet hat noch ...

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());

Gesehene Werte sollten in einer Menge gespeichert werden, nicht im Array (der Ersetzer wird für jedes Element genannt), und es ist nicht erforderlich, JSON.stringifyjedes Element in der Kette zu versuchen, die zu einem Zirkelverweis führt .

Wie in der akzeptierten Antwort entfernt diese Lösung alle sich wiederholenden Werte, nicht nur die kreisförmigen. Aber zumindest ist es nicht exponentiell komplex.

26
Artur Klesun

Ich würde empfehlen, json-stringify-safe von @ isaacs- zu überprüfen - es wird in NPM verwendet.

Übrigens: Wenn Sie Node.js nicht verwenden, können Sie die Zeilen 4-27 einfach aus dem relevanten Teil des Quellcodes kopieren und einfügen.

Installieren:

$ npm install json-stringify-safe --save

Benutzen:

// 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));

Dies ergibt:

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

Beachten Sie, dass Sie, genau wie bei der Vanilla JSON.stringify-Funktion als @Rob W erwähnt, auch das Bereinigungsverhalten anpassen können, indem Sie als zweites Argument an stringify() eine "replace" -Funktion übergeben. Wenn Sie ein einfaches Beispiel benötigen, wie Sie dies tun können, habe ich gerade einen benutzerdefinierten Ersetzer geschrieben, der Fehler, Regexxps und Funktionen in vom Menschen lesbare Zeichenketten hier erzwingt.

21
mikermcneil

Für zukünftige Googler, die nach einer Lösung für dieses Problem suchen, wenn Sie die Schlüssel aller Zirkelverweise nicht kennen , können Sie einen Wrapper um den JSON verwenden. Stringify-Funktion zum Ausschließen von Zirkelverweisen. Ein Beispielskript finden Sie unter https://Gist.github.com/4653128 .

Die Lösung besteht im Wesentlichen darin, einen Verweis auf zuvor gedruckte Objekte in einem Array zu behalten und diesen in einer Ersetzungsfunktion zu überprüfen, bevor ein Wert zurückgegeben wird. Dies ist einschränkender als nur das Ausschließen von Zirkelverweisen, da auch das zweimalige Drucken eines Objekts ausgeschlossen ist, bei dem Zirkelverweise vermieden werden.

Beispiel Wrapper:

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));

bewertet zu:

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

mit der Funktion:

/**
 * 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

Verwenden Sie die JSON.stringify-Methode mit einem Ersetzungsprogramm. Lesen Sie diese Dokumentation für weitere Informationen. http://msdn.Microsoft.com/de-de/library/cc836459%28v=vs.94%29.aspx

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

var replacement = {"b":undefined};

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

Finden Sie eine Möglichkeit, das Ersatz-Array mit zyklischen Referenzen aufzufüllen. Sie können die typeof-Methode verwenden, um herauszufinden, ob eine Eigenschaft vom Typ 'object' (reference) ist, und eine exakte Gleichheitsprüfung (===) zur Überprüfung der Zirkelreferenz.

4
TWickz

Ich weiß, dass dies eine alte Frage ist, aber ich möchte ein von mir erstelltes NPM-Paket namens smart -ircular vorschlagen, das anders funktioniert als die anderen vorgeschlagenen Möglichkeiten. Dies ist besonders nützlich, wenn Sie große und tiefe Objekte verwenden. 

Einige Funktionen sind:

  • Ersetzen kreisförmiger Referenzen oder einfach wiederholter Strukturen innerhalb des Objekts durch den Pfad, der zu seinem ersten Vorkommen führt (nicht nur die Zeichenfolge [kreisförmig]);

  • Durch die Suche nach Zirkularitäten in einer Breitensuche wird sichergestellt, dass dieser Pfad so klein wie möglich ist, was beim Umgang mit sehr großen und tiefen Objekten wichtig ist, bei denen die Pfade ärgerlich lang werden und schwer zu verfolgen sind (der benutzerdefinierte Ersatz in JSON.stringify führt ein DFS durch);

  • Ermöglicht personalisierte Ersetzungen, praktisch, um weniger wichtige Teile des Objekts zu vereinfachen oder zu ignorieren.

  • Schließlich werden die Pfade genau so geschrieben, wie es für den Zugriff auf das referenzierte Feld erforderlich ist. Dies kann Ihnen beim Debuggen helfen.

3
Danilo Augusto

Ich löse dieses Problem wie folgt:

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

Mit dem zweiten Argument für JSON.stringify () auch können Sie ein Array von Schlüsselnamen angeben, das von jedem Objekt in Ihren Daten beibehalten werden soll. Dies funktioniert möglicherweise nicht für alle Anwendungsfälle, ist jedoch eine wesentlich einfachere Lösung.

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"}

Hinweis: Seltsamerweise wirft die Objektdefinition von OP im aktuellen Chrome oder Firefox keinen Zirkelreferenzfehler auf. Die Definition in dieser Antwort wurde so geändert, dass did einen Fehler auslöst.


1
Aaron Cicali

Basierend auf den anderen Antworten bekomme ich den folgenden Code. Es funktioniert ziemlich gut mit Zirkelverweisen, Objekten mit benutzerdefinierten Konstruktoren.

Von dem angegebenen Objekt, das serialisiert werden soll, 

  • Zwischenspeichern Sie das gesamte Objekt, auf das Sie beim Durchlaufen des Objekts stoßen, und weisen Sie jedem Objekt eine eindeutige Hash-ID zu (eine automatisch zunehmende Nummer funktioniert auch)
  • Sobald ein Zirkelverweis gefunden wurde, markieren Sie dieses Feld im neuen Objekt als Zirkular und speichern Sie die Hash-ID des ursprünglichen Objekts als Attribut.

Github Link - 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;

Beispielverwendung 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));

Beispielverwendung 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

eine andere Lösung zum Lösen dieses Problems mit diesen Objekten ist die Verwendung dieser Bibliothek

https://github.com/ericmuyser/stringy

es ist einfach und Sie können dies in wenigen Schritten lösen.

0
Ehsan Aghaei

Obwohl dies ausreichend beantwortet wurde, können Sie die betreffende Eigenschaft auch explizit löschen, bevor Sie sie mit dem Operator delete stringifizieren.

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

Operator löschen

dadurch entfällt die Notwendigkeit, komplexe Logik zum Entfernen von Zirkelverweisen zu erstellen oder beizubehalten.

0

Verwenden Sie circular-json (veraltet) nicht, um die Antwort auf das Überschreiben der Funktionsweise von JSON zu aktualisieren (wahrscheinlich nicht empfohlen, aber sehr einfach). Verwenden Sie stattdessen den Nachfolger, abgeflacht:

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

Aus der alten Antwort oben von @ user1541685 entlehnt, aber durch die neue ersetzt:

npm i --save flatted

dann in deiner js datei

const JSON = require('flatted');

...

const json = JSON.stringify(obj);

Das könntest du auch tun

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

Ich weiß, dass diese Frage alt ist und viele tolle Antworten bietet, aber ich poste diese Antwort wegen ihres neuen Geschmacks (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

Ich fand kreisförmige Json-Bibliothek auf Github und es funktionierte gut für mein Problem.

Einige gute Funktionen, die ich nützlich fand:

  • Unterstützt die Verwendung auf mehreren Plattformen, aber ich habe es bisher nur mit node.js getestet.
  • Die API ist die gleiche, Sie müssen sie also nur als JSON-Ersatz einsetzen und verwenden.
  • Es hat eine eigene Analysemethode, so dass Sie die 'zirkularen' serialisierten Daten wieder in ein Objekt konvertieren können.
0
JacopKane

Ob 

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

führt zu einem 

TypeError: zyklischer Objektwert

Dann möchten Sie vielleicht so drucken:

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

Versuche dies:

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