是不是可以使用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.stringify
string本地错误消息? 如果没有,为什么会发生这种行为?
绕过这个的方法
- 坚持简单的基于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
。
它处理甚至嵌套的错误对象,我实际上我需要在我的项目很多。