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.

474voto

laggingreflex Punkte 29759
JSON.stringify(err, Object.getOwnPropertyNames(err))

scheint zu funktionieren

[aus einem Kommentar von /u/ub3rgeek auf /r/javascript] und felixfbeckers Kommentar unten

248voto

Jonathan Lonowski Punkte 116832

Sie können ein Error.prototype.toJSON definieren, um ein einfaches Object darzustellen, das den Error repräsentiert:

if (!('toJSON' in Error.prototype))
Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
        var alt = {};

        Object.getOwnPropertyNames(this).forEach(function (key) {
            alt[key] = this[key];
        }, this);

        return alt;
    },
    configurable: true,
    writable: true
});

var error = new Error('testing');
error.detail = 'foo bar';

console.log(JSON.stringify(error));
// {"message":"testing","detail":"foo bar"}

Die Verwendung von Object.defineProperty() fügt toJSON hinzu, ohne dass es selbst eine enumerable Eigenschaft ist.


Was die Modifizierung von Error.prototype betrifft, so ist toJSON() möglicherweise nicht speziell für Errors definiert, jedoch ist die Methode für Objekte im Allgemeinen standardisiert (Referenz: Schritt 3). Daher ist das Risiko von Kollisionen oder Konflikten minimal.

Jedoch kann zur vollständigen Vermeidung trotzdem der replacer-Parameter von JSON.stringify() verwendet werden:

function replaceErrors(key, value) {
    if (value instanceof Error) {
        var error = {};

        Object.getOwnPropertyNames(value).forEach(function (propName) {
            error[propName] = value[propName];
        });

        return error;
    }

    return value;
}

var error = new Error('testing');
error.detail = 'foo bar';

console.log(JSON.stringify(error, replaceErrors));

221voto

Sanghyun Lee Punkte 19536

Da niemand über den warum-Teil spricht, werde ich ihn beantworten.

Warum gibt JSON.stringify ein leeres Objekt zurück?

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

Antwort

Laut der Dokumentation von JSON.stringify(),

Für alle anderen Objektinstanzen (einschließlich Map, Set, WeakMap und WeakSet) werden nur ihre aufzählbaren Eigenschaften serialisiert.

Und das Error-Objekt hat keine aufzählbaren Eigenschaften, daher wird ein leeres Objekt ausgegeben.

90voto

Lukasz Czerwinski Punkte 11167

Es gibt ein tolles Node.js-Paket dafür: serialize-error.

npm install serialize-error

Es behandelt sogar verschachtelte Fehlerobjekte gut.

import {serializeError} from 'serialize-error';

const stringifiedError = serializeError(error);

Dokumentation: https://www.npmjs.com/package/serialize-error

69voto

Bryan Larsen Punkte 8886

Ändern von Jonathan's großartiger Antwort, um Monkey Patching zu vermeiden:

var stringifyError = function(err, filter, space) {
  var plainObject = {};
  Object.getOwnPropertyNames(err).forEach(function(key) {
    plainObject[key] = err[key];
  });
  return JSON.stringify(plainObject, filter, space);
};

var error = new Error('Testing');
error.detail = 'foo bar';

console.log(stringifyError(error, null, '\t'));

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