尝试使用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至少尝试了一次,但忽略了以下消息:
>>> JSON.stringify(error); // Firebug, Firefox 23 {"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
pre >
替换函数
然后我查看了Error.prototype。它表明原型包含诸如toString和toSource之类的方法。知道函数不能被字符串化后,我在调用JSON.stringify删除所有函数时包括了一个替换函数,但随后意识到它也具有一些怪异的行为:
var error = new Error('simple error message'); JSON.stringify(error, function(key, value) { console.log(key === ''); // true (?) console.log(value === error); // true (?) });
它似乎并没有像往常那样循环遍历对象,因此我无法检查键是否为函数并忽略它。
问题
是否可以用JSON.stringify
来对本机错误消息进行字符串化?如果不是,为什么会出现这种现象?
解决此问题的方法
粘贴基于简单字符串的错误消息,或者创建个人错误对象并不要不要依赖于本机的Error对象。
拉动属性:JSON.stringify({ message: error.message, stack: error.stack })
更新
@Ray Toal在评论中建议我看看在属性描述符中。现在很清楚为什么它不起作用:
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
。
接受的答案提供了解决此问题的方法。
#1 楼
您可以定义Error.prototype.toJSON
来检索表示Object
的普通Error
:使用Object.defineProperty()
,
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"}
toJSON
本身并不具有enumerable
属性。
关于修改Error.prototype
,虽然可能没有为toJSON()
专门定义Error
,但是该方法通常还是针对对象进行了标准化(参考:step 3)。因此,发生冲突或冲突的风险很小。
尽管为了完全避免冲突,可以使用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));
评论
如果使用.getOwnPropertyNames()而不是.keys(),则将获得不可枚举的属性,而无需手动定义它们。
–user2437417
13年8月22日在21:56
最好不要将其添加到Error.prototype中,否则在将来的JavaScrip版本中Error.prototype实际上具有toJSON函数时可能会引起问题。
–乔·德·琼(Jos de Jong)
2014年12月2日,11:34
小心!此解决方案打破了本机节点mongodb驱动程序中的错误处理:jira.mongodb.org/browse/NODE-554
–塞巴斯蒂安·诺瓦克(Sebastian Nowak)
16-2-25在15:34
万一有人注意自己的链接器错误和命名冲突:如果使用replacer选项,则应为函数replaceErrors(key,value)中的键选择一个不同的参数名称,以避免与.forEach(function(key){的命名冲突。 。});在此答案中未使用replaceErrors键参数。
–找不到404
17-4-20在22:58
在此示例中,键的阴影虽然被允许,但可能会造成混淆,因为它使人们怀疑作者是否打算引用外部变量。对于内部循环,propName将是一个更具表达性的选择。 (顺便说一句,我认为@ 404NotFound的意思是“ linter”(静态分析工具),而不是“ linker”。)在任何情况下,使用自定义替换函数都是一个很好的解决方案,因为它可以在一个适当的位置解决问题,并且不会改变本地/全局行为。
–雅各布
19-09-20在13:15
#2 楼
JSON.stringify(err, Object.getOwnPropertyNames(err))
似乎可以工作
[摘自/ u / ub3rgeek在/ r / javascript上的评论]和felixfbecker在下面的评论评论
梳理答案,JSON.stringify(err,Object.getOwnPropertyNames(err))
–felixfbecker
16年1月6日在13:47
这对于本机ExpressJS Error对象工作正常,但不适用于Mongoose错误。猫鼬错误具有ValidationError类型的嵌套对象。这不会对ValidationError类型的Mongoose错误对象中的嵌套错误对象进行字符串化处理。
–蒸汽动力
16年3月14日在22:49
这应该是答案,因为这是最简单的方法。
–欢
16-10-6在10:23
@felixfbecker只查找一级的属性名称。如果您有var spam = {a:1,b:{b:2,b2:3}};并运行Object.getOwnPropertyNames(spam),您将得到[“ a”,“ b”]-在这里具有欺骗性,因为b对象拥有它自己的b。您可能会在stringify通话中同时遇到这两个问题,但是会错过spam.b.b2。那很糟。
–松饼
17年6月2日在15:07
@ruffin是正确的,但它甚至是可取的。我认为OP想要的只是确保消息和堆栈包含在JSON中。
–felixfbecker
17年7月23日在15:37#3 楼
由于没有人在谈论为什么原因,我要回答。
为什么此JSON.stringify
返回空对象?
> JSON.stringify(error); '{}'
回答
从JSON.stringify()文档中,
对于所有其他Object实例(包括Map,Set,WeakMap和WeakSet),仅其可枚举的属性将被序列化。
,并且Error
对象没有其可枚举的属性,这就是为什么它打印一个空对象的原因。评论
奇怪的是没有人打扰。只要修复有效,我就认为:)
–伊利亚·切尔诺莫迪克(Ilya Chernomordik)
18年8月6日在12:02
该答案的第一部分不正确。有一种使用其replacer参数使用JSON.stringify的方法。
–托德·查菲(Todd Chaffee)
19年6月1日在21:37
@ToddChaffee很好。我已经确定了答案。请检查并随时进行改进。谢谢。
–李圣贤
19年6月2日在9:20#4 楼
修改乔纳森(Jonathan)的最佳答案以避免猴子打补丁:
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'));
评论
第一次我听到猴子打补丁:)
–克里斯·普林斯
16年6月18日在17:27
@ChrisPrince但这不是最后一次了,特别是在JavaScript中!这是有关猴子补丁的Wikipedia,仅供将来的人们参考。 (根据Chris的理解,在乔纳森的答案中,您正在直接向Error的原型添加JSON新功能,这通常不是一个好主意。也许已经有人对此进行了检查,但是您不知道要做什么。其他版本的版本。或者,如果有人意外获得了您的版本,或者假设Error的原型具有特定的属性,那么事情可能会变得很糟糕。)
–松饼
17年6月6日在15:22
很好,但是省略了错误堆栈(在控制台中显示)。不确定细节,如果这是与Vue相关或什么,只想提一下。
– phil294
19年3月13日在23:54
#5 楼
有一个很棒的Node.js软件包:serialize-error
。
npm install serialize-error
它甚至可以很好地处理嵌套的Error对象。
import {serializeError} from 'serialize-error'; JSON.stringify(serializeError(error));
文档:https://www.npmjs.com / package / serialize-error评论
否,但是可以进行翻译。看到这个评论。
–雅各布
19-09-20在13:59
这是正确的答案。序列化错误不是一个小问题,库的作者(一个出色的开发人员,拥有许多受欢迎的软件包)竭尽全力来处理极端情况,如README所示:“保留了自定义属性。不可枚举属性保持不可枚举(名称,消息,堆栈)。可枚举的属性保持枚举(除不可枚举的所有属性)。处理循环引用。”
– Dan Dascalescu
5月19日5:04
#6 楼
您也可以将那些不可枚举的属性重新定义为可枚举。
Object.defineProperty(Error.prototype, 'message', { configurable: true, enumerable: true });
也许还有stack
属性。评论
不要更改您不拥有的对象,这可能会破坏应用程序的其他部分,并祝您好运。
–fregante
19 Mar 17 '19 4:58
#7 楼
我们需要序列化一个任意的对象层次结构,其中层次结构中的根或任何嵌套属性都可以是Error的实例。
我们的解决方案是使用replacer
的JSON.stringify()
参数,例如:
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))
#8 楼
我正在为日志追加程序处理JSON格式,最终在这里试图解决类似的问题。过了一会儿,我意识到我可以让Node来完成这项工作:
const util = require("util"); ... return JSON.stringify(obj, (name, value) => { if (value instanceof Error) { return util.format(value); } else { return value; } }
评论
它应该是instanceof,而不是instanceOf。
–lakshman.pasala
4月22日9:59#9 楼
上面的答案似乎都没有正确序列化Error原型上的属性(因为getOwnPropertyNames()
不包括继承的属性)。我也无法像建议的答案之一那样重新定义属性。
这是我想出的解决方案-它使用lodash,但可以用这些函数的通用版本替换lodash。 />
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中进行的测试:
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}}}
#10 楼
您可以使用普通javascript的单线(errStringified)解决此问题:
var error = new Error('simple error message'); var errStringified = (err => JSON.stringify(Object.getOwnPropertyNames(Object.getPrototypeOf(err)).reduce(function(accumulator, currentValue) { return accumulator[currentValue] = err[currentValue], accumulator}, {})))(error); console.log(errStringified);
它也可以与DOMExceptions一起使用。评论
那是一个很长的班轮!
– Ishan Madhusanka
9月30日下午5:43
哦,是的,它很漂亮。泪流满面,我感到非常自豪。
– savram
9月30日13:04#11 楼
使其可序列化
// example error let err = new Error('I errored') // one liner converting Error into regular object that can be stringified err = Object.getOwnPropertyNames(err).reduce((acc, key) => { acc[key] = err[key]; return acc; }, {})
如果要从子进程,工作进程或通过网络发送此对象,则无需进行字符串化。它将像其他任何普通对象一样自动进行字符串化和解析
评论
您是否检查了错误对象中属性的属性描述符?对我来说,问题是“为什么”,我发现答案在问题的底部。为您自己的问题发布答案没有错,这样您将获得更多的信誉。 :-)
serialize-error程序包将为您处理此问题:npmjs.com/package/serialize-error