Reproduciendo el problema
Me encuentro con un problema al intentar pasar mensajes de error usando los sockets web. Puedo replicar el problema que estoy usando JSON.stringifypara atender a un público más amplio:
// 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, curiosamente, Firefox por lo menos hace un intento pero dejó el mensaje:
>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
Función de reemplazo
Luego miré el prototipo Error.prototype . Muestra que el prototipo contiene métodos como toString y toSource . Sabiendo que las funciones no pueden ser encadenadas, 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 (?)
});
No parece recorrer 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 stringificar mensajes de error nativos con JSON.stringify? Si no, ¿por qué ocurre este comportamiento?
Métodos para evitar esto
- Quédese con mensajes de error simples basados en cadenas, o cree objetos de error personales y no confíe en el objeto de error nativo.
- Propiedades de extracción:
JSON.stringify({ message: error.message, stack: error.stack })
Actualizaciones
@ Ray Toal Sugerido en un comentario que eche un vistazo a los descriptores de propiedades . Ahora está claro 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.
fuente

Respuestas:
Puede definir un
Error.prototype.toJSONpara recuperar un plano queObjectrepresenta elError:Usar
Object.defineProperty()agregatoJSONsin que sea unaenumerablepropiedad en sí misma.Con respecto a la modificación
Error.prototype, aunquetoJSON()puede no estar definidoErrorespecíficamente para s, el método todavía está estandarizado para los objetos en general (ref: paso 3). Por lo tanto, el riesgo de colisiones o conflictos es mínimo.Sin embargo, todavía a evitarlo por completo,
JSON.stringify()'sreplacerparámetro se puede utilizar en su lugar:fuente
.getOwnPropertyNames()lugar de.keys(), obtendrá las propiedades no enumerables sin tener que definirlas manualmente.keydefunction replaceErrors(key, value)evitar conflicto de nombres con.forEach(function (key) { .. }); elreplaceErrorskeyparámetro no se usa en esta respuesta.keyen este ejemplo, aunque está permitido, es potencialmente confuso ya que deja dudas sobre si el autor pretendía referirse a la variable externa o no.propNamesería una opción más expresiva para el bucle interno. (Por cierto, creo que @ 404NotFound significa "linter" (herramienta de análisis estático) no "enlazador" ) En cualquier caso, el uso de unareplacerfunción personalizada es una solución excelente para esto, ya que resuelve el problema en un lugar apropiado y no altera el nativo / comportamiento global.parece funcionar
[ de un comentario de / u / ub3rgeek en / r / javascript ] y el comentario de felixfbecker a continuación
fuente
JSON.stringify(err, Object.getOwnPropertyNames(err))ValidationErrortipos. Esto no stringificará elerrorsobjeto anidado en un objeto de error Mongoose de tipoValidationError.var spam = { a: 1, b: { b: 2, b2: 3} };y corresObject.getOwnPropertyNames(spam), serás["a", "b"]engañoso aquí, porque elbobjeto tiene lo suyob. Obtendría ambos en su llamada de stringify, pero se perderíaspam.b.b2. Eso es malo.messageystackestán incluidos en el JSON.Como nadie está hablando de la parte del porqué , la responderé.
¿Por qué esto
JSON.stringifydevuelve un objeto vacío?Responder
Del documento de JSON.stringify () ,
y el
Errorobjeto no tiene sus propiedades enumerables, es por eso que imprime un objeto vacío.fuente
JSON.stringifyusando sureplacerparámetro.Modificando la gran respuesta de Jonathan para evitar parches de monos:
fuente
monkey patching:)toJSON, directamente alErrorprototipo 's , que a menudo no es una gran idea. Tal vez alguien ya tiene, que este cheques, pero luego no saben qué esa otra versión sí. O si alguien inesperadamente consigue el tuyo, o asume que el prototipo de Error tiene propiedades específicas, las cosas podrían funcionar.)Hay un gran paquete Node.js para que:
serialize-error.Maneja bien incluso los objetos de error anidados, lo que realmente necesitaba mucho en mi proyecto.
https://www.npmjs.com/package/serialize-error
fuente
También puede redefinir esas propiedades no enumerables para que sean enumerables.
y quizás
stackpropiedad también.fuente
Necesitábamos serializar una jerarquía de objetos arbitraria, donde la raíz o cualquiera de las propiedades anidadas en la jerarquía podrían ser instancias de Error.
Nuestra solución fue usar el
replacerparámetro deJSON.stringify(), por ejemplo:fuente
Ninguna de las respuestas anteriores parece serializar correctamente las propiedades que están en el prototipo de Error (porque
getOwnPropertyNames()no incluye las 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.
Aquí está la prueba que hice en Chrome:
fuente
Estaba trabajando en un formato JSON para agregadores de registro y terminé aquí tratando de resolver un problema similar. Después de un tiempo, me di cuenta de que podía hacer que Node hiciera el trabajo:
fuente
instanceofy noinstanceOf.