try parse handling error ejemplo catch javascript json node.js error-handling

parse - try catch javascript



¿No es posible establecer un error al usar JSON.stringify? (7)

Reproduciendo el problema

Tengo un problema al intentar pasar mensajes de error usando sockets web. Puedo replicar el problema al que me enfrento usando JSON.stringify para atender a una audiencia más amplia:

// 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); ''{}''

El problema es que termino con un objeto vacío.

Lo que he intentado

Navegadores

Primero intenté dejar node.js y ejecutarlo en varios navegadores. La versión 28 de Chrome me da el mismo resultado y, lo que es más interesante, Firefox al menos hace un intento, pero dejó de lado el mensaje:

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

Función de reemplazo

Entonces miré el Error.prototype . Muestra que el prototipo contiene métodos como toString y toSource . Sabiendo que las funciones no se pueden clasificar, incluí una función de reemplazo al llamar a JSON.stringify para eliminar todas las funciones, pero luego me di cuenta de que también tenía un comportamiento extraño:

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

Parece que no se repite sobre el objeto como lo haría normalmente, y por lo tanto no puedo verificar si la tecla es una función e ignorarla.

La pregunta

¿Hay alguna forma de estratificar los mensajes de error nativos con JSON.stringify ? Si no es así, ¿por qué ocurre este comportamiento?

Métodos para solucionar esto.

  • Cumpla con simples mensajes de error basados ​​en cadenas, o cree objetos de error personales y no confíe en el objeto de error nativo.
  • Propiedades de JSON.stringify({ message: error.message, stack: error.stack }) : JSON.stringify({ message: error.message, stack: error.stack })

Actualizaciones

@Ray Toal Sugirió en un comentario que echara un vistazo a los descriptores de propiedad . Está claro ahora por qué no funciona:

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

Salida:

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 }

Clave: enumerable: false .

La respuesta aceptada proporciona una solución para este problema.


Como nadie habla de la parte de por qué , voy a responder estas

P: ¿Hay alguna forma de establecer mensajes de error nativos con JSON.stringify?

No.

P: Si no es así, ¿por qué ocurre este comportamiento?

Del documento de JSON.stringify () ,

Para todas las demás instancias de Objeto (incluyendo Mapa, Conjunto, WeakMap y WeakSet), solo sus propiedades enumerables serán serializadas.

y el objeto Error no tiene sus propiedades enumerables, por eso imprime un objeto vacío.



Modificando la gran respuesta de Jonathan para evitar parches de monos:

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''));


Ninguna de las respuestas anteriores pareció serializar correctamente las propiedades que se encuentran en el prototipo de Error (porque getOwnPropertyNames() no incluye propiedades heredadas). Tampoco pude redefinir las propiedades como una de las respuestas sugeridas.

Esta es la solución que se me ocurrió: utiliza lodash, pero podría reemplazarlo con versiones genéricas de esas funciones.

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

Aquí está la prueba que hice en Chrome:

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


Puede definir un Error.prototype.toJSON para recuperar un Object simple que representa el Error :

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

El uso de Object.defineProperty() agrega a toJSON sin que sea una propiedad enumerable .

Con respecto a la modificación de Error.prototype , mientras que toJSON() no se puede definir específicamente para los Error , el método aún está estandarizado para los objetos en general (ref: paso 3). Por lo tanto, el riesgo de colisiones o conflictos es mínimo.

Sin embargo, para evitarlo completamente, el parámetro de replacer JSON.stringify() se puede usar en su lugar:

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


También puede simplemente redefinir esas propiedades no enumerables para que sean enumerables.

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

y tal vez stack propiedad también.