是不是可以使用JSON.stringifystring化错误?

重现问题

我试图通过使用Web套接字传递错误消息时遇到了一个问题。 我可以使用JSON.stringify复制我正面临的问题,以迎合更广泛的受众:

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

问题是我最终得到一个空的对象。

我试过了

浏览器

我第一次尝试离开node.js并在各种浏览器中运行它。 Chrome版本28给了我相同的结果,有趣的是,Firefox至less做了一个尝试,但是忽略了这个消息:

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

替代function

然后我看了Error.prototype 。 它显示了原型包含toString和toSource等方法。 知道函数不能被string化,当调用JSON.stringify去除所有函数时,我包含了一个replace函数,但是后来意识到它也有一些奇怪的行为:

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

它似乎没有像往常一样循环对象,因此我无法检查该键是否是一个函数并忽略它。

问题

有没有什么办法使JSON.stringifystring本地错误消息? 如果没有,为什么会发生这种行为?

绕过这个的方法

  • 坚持简单的基于string的错误消息,或创build个人错误对象,不要依赖本地错误对象。
  • 拉属性: JSON.stringify({ message: error.message, stack: error.stack })

更新

@Ray Toalbuild议在我看看属性描述符的评论 。 现在很清楚为什么它不起作用:

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

输出:

 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 } 

键: enumerable: false

接受的答案提供了解决此问题的方法。

您可以定义一个Error.prototype.toJSON来检索一个表示Error的普通Object

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

使用Object.defineProperty()添加到Object.defineProperty()而不是它本身的enumerable属性。


关于修改Error.prototype ,虽然toJSON()可能没有专门为Error定义, 但是对于一般对象来说方法仍然是标准化的(参考步骤3)。 所以,冲突或冲突的风险是最小的。

尽pipe如此,为了完全避免它,可以使用JSON.stringify()replacer参数 :

 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)); 
 JSON.stringify(err, Object.getOwnPropertyNames(err)) 

似乎工作

[ 来自/ u / ub3rgeek on / r / javascript ]和felixfbecker的评论如下

修改乔纳森避免猴子修补的伟大答案:

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

你也可以重新定义那些不可枚举的属性是可枚举的。

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

也可能是stack属性。

上面的答案都没有正确序列化错误原型上的属性(因为getOwnPropertyNames()不包括inheritance的属性)。 我也无法像build议的答案之一那样重新定义属性。

这是我提出的解决scheme – 它使用lodash,但你可以用这些函数的通用版本replacelodash。

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

以下是我在Chrome中进行的testing:

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

有一个很棒的Node.js包: serialize-error

它处理甚至嵌套的错误对象,我实际上我需要在我的项目很多。

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