555 Stimmen

Ist es nicht möglich, einen Fehler mit JSON.stringify zu stringifizieren?

Reproduktion des Problems

Ich stoße auf ein Problem, wenn ich versuche, Fehlermeldungen über Websockets weiterzugeben. Ich kann das Problem, dem ich gegenüberstehe, mittels JSON.stringify reproduzieren, um eine breitere Zielgruppe anzusprechen:

// node v0.10.15
> var error = new Error('einfache Fehlermeldung');
    undefined

> error
    [Error: einfache Fehlermeldung]

> Object.getOwnPropertyNames(error);
    [ 'stack', 'arguments', 'type', 'message' ]

> JSON.stringify(error);
    '{}'

Das Problem ist, dass ich am Ende ein leeres Objekt erhalte.

Was ich versucht habe

Browser

Zuerst habe ich node.js verlassen und es in verschiedenen Browsern ausgeführt. Chrome-Version 28 liefert mir das gleiche Ergebnis, und interessanterweise macht Firefox zumindest einen Versuch, aber ließ die Nachricht aus:

>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}

Replacer-Funktion

Dann habe ich mir das Error.prototype angesehen. Es zeigt, dass das Prototyp Methoden wie toString und toSource enthält. Da Funktionen nicht in Strings umgewandelt werden können, habe ich eine Replacer-Funktion beim Aufrufen von JSON.stringify eingefügt, um alle Funktionen zu entfernen, bemerkte aber dann, dass auch dies ein seltsames Verhalten zeigte:

var error = new Error('einfache Fehlermeldung');
JSON.stringify(error, function(key, value) {
    console.log(key === ''); // true (?)
    console.log(value === error); // true (?)
});

Es scheint nicht über das Objekt zu iterieren wie es normalerweise tun würde, und daher kann ich nicht überprüfen, ob der Schlüssel eine Funktion ist und ihn ignorieren.

Die Frage

Gibt es einen Weg, nativ Error-Meldungen mit JSON.stringify zu stringifizieren? Wenn nicht, warum tritt dieses Verhalten auf?

Wege, um dieses Problem zu umgehen

  • Bei einfachen stringbasierten Fehlermeldungen bleiben oder persönliche Fehlerobjekte erstellen und nicht auf das native Error-Objekt verlassen.
  • Eigenschaften abrufen: JSON.stringify({ message: error.message, stack: error.stack })

Updates

@Ray Toal schlug in einem Kommentar vor, dass ich mir die Eigenschaftsdeskriptoren anschaue. Es ist jetzt klar, warum es nicht funktioniert:

var error = new Error('einfache Fehlermeldung');
var propertyNames = Object.getOwnPropertyNames(error);
var descriptor;
for (var property, i = 0, len = propertyNames.length; i < len; ++i) {
    property = propertyNames[i];
    descriptor = Object.getOwnPropertyDescriptor(error, property);
    console.log(property, descriptor);
}

Ausgabe:

stack { get: [Function],
  set: [Function],
  enumerable: false,
  configurable: true }
arguments { value: undefined,
  writable: true,
  enumerable: false,
  configurable: true }
type { value: undefined,
  writable: true,
  enumerable: false,
  configurable: true }
message { value: 'einfache Fehlermeldung',
  writable: true,
  enumerable: false,
  configurable: true }

Schlüssel: enumerable: false.

Die akzeptierte Antwort bietet eine Lösung für dieses Problem.

13voto

Joel Malone Punkte 1184

Wir mussten eine beliebige Objekthierarchie serialisieren, bei der die Wurzel oder eine der verschachtelten Eigenschaften in der Hierarchie Instanzen von Error sein könnte.

Unsere Lösung bestand darin, den replacer-Parameter von JSON.stringify() zu verwenden, z. B.:

function jsonFriendlyErrorReplacer(key, value) {
  if (value instanceof Error) {
    return {
      // Alle aufzählbaren Eigenschaften abrufen, die die Eigenschaften auf benutzerdefinierten Fehlern unterstützen
      ...value,
      // Explizit die nicht aufzählbaren Eigenschaften von Error abrufen
      name: value.name,
      message: value.message,
      stack: value.stack,
    }
  }

  return value
}

let obj = {
    error: new Error('verschachtelte Fehlermeldung')
}

console.log('Ergebnis OHNE benutzerdefinierten Replacer:', JSON.stringify(obj))
console.log('Ergebnis MIT benutzerdefinierten Replacer:', JSON.stringify(obj, jsonFriendlyErrorReplacer))

13voto

Jason Punkte 2621

Ich habe an einem JSON-Format für Log Appenders gearbeitet und bin hier gelandet, während ich versuchte, ein ähnliches Problem zu lösen. Nach einer Weile habe ich realisiert, dass ich Node einfach die Arbeit erledigen lassen könnte:

const util = require("util");
...
return JSON.stringify(obj, (name, value) => {
    if (value instanceof Error) {
        return util.format(value);
    } else {
        return value;
    }
}

10voto

David Navrkal Punkte 414

Wenn Sie nodejs verwenden, gibt es einen besseren zuverlässigen Weg, indem Sie das native nodejs inspect verwenden. Sie können auch angeben, Objekte in unbegrenzter Tiefe zu drucken.

Beispiel in Typescript:

import { inspect }  from "util";

const myObject = new Error("Das ist ein Fehler");
console.log(JSON.stringify(myObject)); // Wird {} ausgeben
console.log(myObject); // Wird das vollständige Fehlerobjekt ausgeben
console.log(inspect(myObject, {depth: null})); // Derselbe Ausgang wie console.log plus es funktioniert auch für Objekte mit vielen verschachtelten Eigenschaften.

Link zur Dokumentation, Link zum Beispiel für die Verwendung.

Und wie auch in dem Thema diskutiert Wie erhalte ich das vollständige Objekt in Node.js-Konsole.log() anstatt '[Object]'? hier in Stack Overflow.

9voto

cheolgook Punkte 107

Sie können auch einfach diese nicht aufzählbaren Eigenschaften neu definieren, damit sie aufzählbar sind.

Object.defineProperty(Error.prototype, 'message', {
    configurable: true,
    enumerable: true
});

und vielleicht auch die stack-Eigenschaft.

7voto

Deep Panchal Punkte 19

Konstruktor der Zeichenfolge sollte in der Lage sein, einen Fehler in eine Zeichenfolge umzuwandeln

try { 
  throw new Error("MY ERROR MSG")
} catch (e) {
  String(e) // gibt 'Error: MY ERROR MSG' zurück
}

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