Is it not possible to stringify an Error using JSON.stringify?
我在尝试使用Web套接字传递错误消息时遇到了一个问题。我可以用
1 2 3 4 5 6 7 8 9 10 11 12 | // 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并在各种浏览器中运行它。Chrome28版给了我同样的结果,有趣的是,火狐至少尝试了一下,但忽略了以下信息:
1 2 3 | >>> JSON.stringify(error); // Firebug, Firefox 23 {"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1 "} |
替换程序功能
然后我查看了错误。原型。结果表明,原型包含ToString和ToSource等方法。我知道函数不能串化,所以在调用json.stringify删除所有函数时,我包含了一个replacer函数,但后来意识到它也有一些奇怪的行为:
1 2 3 4 5 | var error = new Error('simple error message'); JSON.stringify(error, function(key, value) { console.log(key === ''); // true (?) console.log(value === error); // true (?) }); |
它看起来不像通常那样循环对象,因此我无法检查键是否是函数并忽略它。
问题有没有办法用
- 坚持使用简单的基于字符串的错误消息,或者创建个人错误对象,而不依赖于本机错误对象。
- 拉伸性能:
JSON.stringify({ message: error.message, stack: error.stack }) 。
更新
@RayToal在评论中建议我看一下属性描述符。现在很清楚为什么它不起作用:
1 2 3 4 5 6 7 8 | 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); } |
输出:
4密钥:
接受的答案为这个问题提供了解决方法。
1 | JSON.stringify(err, Object.getOwnPropertyNames(err)) |
似乎工作
[来自/u/ub3rgeek on/r/javascript的评论]和下面的felixfbecker的评论
可以定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 }); |
1 2 3 4 5 | var error = new Error('testing'); error.detail = 'foo bar'; console.log(JSON.stringify(error)); // {"message":"testing","detail":"foo bar"} |
使用
关于修改
不过,为了完全避免这种情况,可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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)); |
修改乔纳森的伟大答案以避免猴子修补:
1 2 3 4 5 6 7 8 9 10 11 12 | 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')); |
因为没人在谈论为什么,我要回答这些问题
问:有没有办法用json.stringify将本机错误消息串接起来?
不。
问:如果没有,为什么会发生这种行为?
从json.stringify()的文档中,
For all the other Object instances (including Map, Set, WeakMap and WeakSet), only their enumerable properties will be serialized.
而
有一个很棒的node.js包:
它甚至可以很好地处理嵌套的错误对象,这是我在项目中实际需要的。
https://www.npmjs.com/package/serialize-error
您也可以将那些不可枚举的属性重新定义为可枚举的。
4也可能是
上面的回答似乎都没有正确地序列化错误原型上的属性(因为
这是我提出的解决方案——它使用了lodash,但您可以用这些函数的通用版本替换lodash。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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上做的测试:
1 2 3 4 5 6 7 8 9 10 | 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 at :66:15","causedBy":{"name":"Error","message":"error2","stack":"Error: error2 at :67:20","causedBy":{"name":"Error","message":"error3","stack":"Error: error3 at :68:29","displayed":true}}} |
我们需要序列化一个任意的对象层次结构,其中根或层次结构中的任何嵌套属性都可能是错误的实例。
我们的解决方案是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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)) |