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.
Hay un gran paquete Node.js para eso: serialize-error
.
Maneja objetos de error bien anidados, lo que en realidad necesitaba mucho en mi proyecto.
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.
JSON.stringify(err, Object.getOwnPropertyNames(err))
parece funcionar
[ de un comentario de / u / ub3rgeek en / r / javascript ] y el comentario de felixfbecker a continuación