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.

6voto

Elliott Palermo Punkte 61

Keine der obigen Antworten schien Eigenschaften richtig zu serialisieren, die sich im Prototyp von Error befinden (weil getOwnPropertyNames() keine geerbten Eigenschaften enthält). Ich konnte auch die Eigenschaften nicht neu definieren, wie es eine der Antworten vorschlug.

Das ist die Lösung, die ich gefunden habe - sie verwendet lodash, aber Sie können lodash durch generische Versionen dieser Funktionen ersetzen.

 function recursivePropertyFinder(obj){
    if( obj === Object.prototype){
        return {};
    }else{
        return _.reduce(Object.getOwnPropertyNames(obj), 
            function copy(result, value, key) {
                if( !_.isFunction(obj[value])){
                    if( _.isObject(obj[value])){
                        result[value] = recursivePropertyFinder(obj[value]);
                    }else{
                        result[value] = obj[value];
                    }
                }
                return result;
            }, recursivePropertyFinder(Object.getPrototypeOf(obj)));
    }
}

Error.prototype.toJSON = function(){
    return recursivePropertyFinder(this);
}

Hier ist der Test den ich in Chrome gemacht habe:

var myError = Error('hello');
myError.causedBy = Error('error2');
myError.causedBy.causedBy = Error('error3');
myError.causedBy.causedBy.displayed = true;
JSON.stringify(myError);

{"name":"Error","message":"hello","stack":"Error: hello\n    at :66:15","causedBy":{"name":"Error","message":"error2","stack":"Error: error2\n    at :67:20","causedBy":{"name":"Error","message":"error3","stack":"Error: error3\n    at :68:29","displayed":true}}}

4voto

Pawel Punkte 12592

Einfach in ein reguläres Objekt umwandeln

// Beispiel Fehler
let err = new Error('Fehler aufgetreten')

// Einzeiler, der Error in ein reguläres Objekt umwandelt, das in einen String umgewandelt werden kann
err = Object.getOwnPropertyNames(err).reduce((acc, key) => { acc[key] = err[key]; return acc; }, {})

Wenn Sie dieses Objekt aus einem Child-Prozess, einem Worker oder über das Netzwerk senden möchten, ist kein Stringifizieren erforderlich. Es wird automatisch wie jedes andere normale Objekt in einen String umgewandelt und geparst.

3voto

Elron Punkte 971

Ich habe diese Antwort erweitert: Ist es nicht möglich, einen Fehler mit JSON.stringify zu serialisieren?

serializeError.ts

export function serializeError(err: unknown) {
    return JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err)))
}

Und ich kann es so verwenden:

import { serializeError } from '../helpers/serializeError'; // Ändern Sie Ihren Pfad

try {
    const res = await create(data);
    return { status: 201 };
} catch (err) {
    return { status: 400, error: serializeError(err) };
}

0voto

savram Punkte 446

Sie können dies mit einem Einzeiler (errStringified) in reinem JavaScript lösen:

var error = new Error('einfache Fehlermeldung');
var errStringified = (err => JSON.stringify(Object.getOwnPropertyNames(Object.getPrototypeOf(err)).reduce(function(accumulator, currentValue) { return accumulator[currentValue] = err[currentValue], accumulator}, {})))(error);
console.log(errStringified);

Es funktioniert auch mit DOMExceptions.

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