886 Stimmen

Wie kann ich eine kreisförmige Struktur in einem JSON-ähnlichen Format drucken?

Ich habe ein großes Objekt, das ich in JSON konvertieren und senden möchte. Es hat jedoch eine zirkuläre Struktur, wenn ich also versuche, mit JSON.stringify() Ich werde es bekommen:

TypFehler: Umwandlung einer kreisförmigen Struktur in JSON

o

TypeError: zyklischer Objektwert

Ich möchte alle zirkulären Verweise verwerfen und alles senden, was sich in eine Zeichenkette einfügen lässt. Wie kann ich das tun?

Merci.

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

Ich möchte obj in stringify:

{"a":"foo"}

911voto

Erel Segal-Halevi Punkte 29656

In Node.js können Sie mit util.inspect(object) . Es ersetzt automatisch kreisförmige Links durch "[Kreisförmig]".


Auch wenn sie eingebaut sind (es ist keine Installation erforderlich) 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')

Um sie zu verwenden, rufen Sie einfach

console.log(util.inspect(myObject))

Beachten Sie auch, dass Sie das Objekt options an inspect übergeben können (siehe Link oben)

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

Bitte lesen und loben Sie die Kommentatoren unten...

757voto

Rob W Punkte 327048

使用する JSON.stringify mit einem benutzerdefinierten Ersetzer. Zum Beispiel:

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

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(circ, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    // Duplicate reference found, discard key
    if (cache.includes(value)) return;

    // Store value in our collection
    cache.push(value);
  }
  return value;
});
cache = null; // Enable garbage collection

Die Ersetzung in diesem Beispiel ist nicht 100%ig korrekt (abhängig von Ihrer Definition von "Duplikat"). Im 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, ...);

Aber das Konzept steht: Verwenden Sie einen benutzerdefinierten Ersetzer, und behalten Sie die geparsten Objektwerte im Auge.

Als eine in es6 geschriebene Nutzenfunktion:

// safely handles circular references
JSON.safeStringify = (obj, indent = 2) => {
  let cache = [];
  const retVal = JSON.stringify(
    obj,
    (key, value) =>
      typeof value === "object" && value !== null
        ? cache.includes(value)
          ? undefined // Duplicate reference found, discard key
          : cache.push(value) && value // Store value in our collection
        : value,
    indent
  );
  cache = null;
  return retVal;
};

// Example:
console.log('options', JSON.safeStringify(options))

277voto

Klesun Punkte 9663

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

const circularReference = {otherData: 123};
circularReference.myself = circularReference;

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

const stringified = JSON.stringify(circularReference, getCircularReplacer());

console.log(stringified);

Gesehene Werte sollten gespeichert werden in einer Reihe , nicht in Array (Ersetzer wird aufgerufen für jedes Element ).

Wie in der akzeptierten Antwort wird bei dieser Lösung Folgendes entfernt alle sich wiederholenden Werte und nicht nur die kreisförmigen. Aber zumindest hat es keine exponentielle Komplexität.

86voto

user1541685 Punkte 883

Einfach tun

npm i --save circular-json

dann in Ihrer js-Datei

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

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

HINWEIS: Ich habe nichts mit diesem Paket zu tun. Aber ich verwende es für diese Aufgabe.

Aktualisierung 2020

Bitte beachten Sie, dass CircularJSON nur in der Wartung ist und flach. ist sein Nachfolger.

49voto

guy mograbi Punkte 24995

Die Lösung von Trindaz hat mir sehr gut gefallen - sie war zwar ausführlicher, hatte aber auch einige Fehler. Ich habe sie für alle, die sie auch mögen, behoben.

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

Wenn das zu druckende Objekt 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);
};

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X