webentwicklung-frage-antwort-db.com.de

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

Das Problem wird reproduziert

Ich habe ein Problem, wenn ich versuche, Fehlermeldungen über Web-Sockets zu senden. Ich kann das Problem, mit dem ich konfrontiert bin, mit JSON.stringify wiederholen, um ein breiteres Publikum anzusprechen:

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

> error
    [Error: simple error message]

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

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

Das Problem ist, dass ich mit einem leeren Objekt ende.

Was ich versucht habe

Browser

Ich habe zuerst versucht, node.js zu verlassen und in verschiedenen Browsern auszuführen. Die Chrome-Version 28 liefert mir dasselbe Ergebnis. Interessanterweise versucht Firefox zumindest einen Versuch, lässt aber die Nachricht aus:

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

Replacer-Funktion

Ich habe mir dann den Error.prototype angesehen. Es zeigt, dass der Prototyp Methoden wie toString und toSource enthält. Da ich wusste, dass Funktionen nicht stringifiziert werden können, fügte ich beim Aufruf von JSON.stringify ein Ersetzungsfunktion ein, um alle Funktionen zu entfernen, erkannte jedoch, dass auch sie ein seltsames Verhalten hatte:

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

Es scheint sich nicht wie üblich über das Objekt zu bewegen, und daher kann ich nicht überprüfen, ob der Schlüssel eine Funktion ist, und ihn ignorieren.

Die Frage

Gibt es eine Möglichkeit, native Fehlermeldungen mit JSON.stringify zu stringifizieren? Wenn nicht, warum tritt dieses Verhalten auf?

Methoden, um dies zu umgehen

  • Halten Sie sich an einfache, zeichenfolgenbasierte Fehlermeldungen oder erstellen Sie persönliche Fehlerobjekte und verlassen Sie sich nicht auf das native Fehlerobjekt.
  • Pull-Eigenschaften: JSON.stringify({ message: error.message, stack: error.stack })

Aktualisierung

@Ray Toal In einem Kommentar vorgeschlagen, betrachte ich die Eigenschaftsdeskriptoren . Es ist jetzt klar, warum es nicht funktioniert:

var error = new Error('simple error message');
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: 'simple error message',
  writable: true,
  enumerable: false,
  configurable: true }

Schlüssel: enumerable: false.

Die akzeptierte Antwort bietet eine Problemumgehung für dieses Problem.

225
querie.cc

Sie können einen Error.prototype.toJSON definieren, um einen einfachen Objectnamen__ abzurufen, der den Errornamen__ darstellt:

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

Mit Object.defineProperty() wird toJSONhinzugefügt, ohne dass es sich um eine enumerable-Eigenschaft handelt.


In Bezug auf das Ändern von Error.prototype, während toJSON() möglicherweise nicht speziell für Errorname__s definiert ist, die Methode ist immer noch standardisiert ​​für Objekte im Allgemeinen (siehe Schritt 3). Das Risiko von Kollisionen oder Konflikten ist also minimal.

Um dies dennoch vollständig zu vermeiden, kann stattdessen der Parameter replacervon JSON.stringify() verwendet werden:

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

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

        return error;
    }

    return value;
}

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

console.log(JSON.stringify(error, replaceErrors));
141
JSON.stringify(err, Object.getOwnPropertyNames(err))

scheint zu funktionieren

[ aus einem Kommentar von/u/ub3rgeek zu/r/javascript ] und dem Kommentar von felixfbecker

164
laggingreflex

Jonathans großartige Antwort modifizieren, um das Patchen von Affen 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'));
42
Bryan Larsen

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

Q: Gibt es eine Möglichkeit, native Fehlermeldungen mit JSON.stringify zu stringifizieren?

Nein.

F: Wenn nicht, warum tritt dieses Verhalten auf?

Aus dem Dokument von JSON.stringify ()

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

und Error object hat keine aufzählbaren Eigenschaften, deshalb gibt es ein leeres Objekt aus.

12
Sanghyun Lee

Sie können auch diese nicht aufzählbaren Eigenschaften einfach neu definieren, um sie aufzuzählen.

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

und vielleicht auch stack-Eigenschaft.

9
cheolgook

Dafür gibt es ein tolles Node.js-Paket: serialize-error.

Es behandelt auch verschachtelte Fehlerobjekte gut, was ich eigentlich in meinem Projekt sehr brauchte.

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

7

Keine der obigen Antworten schien Eigenschaften, die sich auf dem Prototyp von Error befinden, richtig zu serialisieren (da getOwnPropertyNames() keine geerbten Eigenschaften enthält). Ich konnte die Eigenschaften auch nicht wie in einer der vorgeschlagenen Antworten neu definieren.

Dies ist die Lösung, die ich mir ausgedacht habe - sie verwendet lodash, aber Sie könnten 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 durchgeführt 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 <anonymous>:66:15","causedBy":{"name":"Error","message":"error2","stack":"Error: error2\n    at <anonymous>:67:20","causedBy":{"name":"Error","message":"error3","stack":"Error: error3\n    at <anonymous>:68:29","displayed":true}}}  
4
Elliott Palermo

Wir mussten eine beliebige Objekthierarchie serialisieren, wobei der Stamm oder eine der verschachtelten Eigenschaften in der Hierarchie Fehler sein könnte.

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

function jsonFriendlyErrorReplacer(key, value) {
  if (value instanceof Error) {
    return {
      // Pull all enumerable properties, supporting properties on custom Errors
      ...value,
      // Explicitly pull Error's non-enumerable properties
      name: value.name,
      message: value.message,
      stack: value.stack,
    }
  }

  return value
}

let obj = {
    error: new Error('nested error message')
}

console.log('Result WITHOUT custom replacer:', JSON.stringify(obj))
console.log('Result WITH custom replacer:', JSON.stringify(obj, jsonFriendlyErrorReplacer))

0
Joel Malone