node.js 是否无法使用 JSON.stringify 将错误字符串化?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18391212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it not possible to stringify an Error using JSON.stringify?
提问by JayQuerie.com
Reproducing the problem
重现问题
I'm running into an issue when trying to pass error messages around using web sockets. I can replicate the issue I am facing using JSON.stringifyto cater to a wider audience:
我在尝试使用 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);
'{}'
The problem is that I end up with an empty object.
问题是我最终得到了一个空对象。
What I've tried
我试过的
Browsers
浏览器
I first tried leaving node.js and running it in various browsers. Chrome version 28 gives me the same result, and interestingly enough, Firefox at least makes an attempt but left out the message:
我首先尝试离开 node.js 并在各种浏览器中运行它。Chrome 版本 28 给了我相同的结果,有趣的是,Firefox 至少进行了尝试,但忽略了消息:
>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
Replacer function
替换功能
I then looked at the Error.prototype. It shows that the prototype contains methods such as toStringand toSource. Knowing that functions can't be stringified, I included a replacer functionwhen calling JSON.stringify to remove all functions, but then realized that it too had some weird behavior:
然后我查看了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 (?)
});
It doesn't seem to loop over the object as it normally would, and therefore I can't check if the key is a function and ignore it.
它似乎不像往常那样循环遍历对象,因此我无法检查键是否为函数并忽略它。
The Question
问题
Is there any way to stringify native Error messages with JSON.stringify? If not, why does this behavior occur?
有没有办法用字符串化本机错误消息JSON.stringify?如果不是,为什么会发生这种行为?
Methods of getting around this
解决这个问题的方法
- Stick with simple string-based error messages, or create personal error objects and don't rely on the native Error object.
- Pull properties:
JSON.stringify({ message: error.message, stack: error.stack })
- 坚持使用简单的基于字符串的错误消息,或者创建个人错误对象,不要依赖于本机 Error 对象。
- 拉属性:
JSON.stringify({ message: error.message, stack: error.stack })
Updates
更新
@Ray ToalSuggested in a comment that I take a look at the property descriptors. It is clear now why it does not work:
@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);
}
Output:
输出:
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 }
Key: enumerable: false.
关键:enumerable: false。
Accepted answer provides a workaround for this problem.
接受的答案提供了解决此问题的方法。
采纳答案by Jonathan Lonowski
You can define a Error.prototype.toJSONto retrieve a plain Objectrepresenting the Error:
您可以定义 aError.prototype.toJSON来检索Object表示 的纯文本Error:
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"}
Using Object.defineProperty()adds toJSONwithout it being an enumerableproperty itself.
使用Object.defineProperty()添加toJSON而不是enumerable属性本身。
Regarding modifying Error.prototype, while toJSON()may not be defined for Errors specifically, the method is still standardizedfor objects in general (ref: step 3). So, the risk of collisions or conflicts is minimal.
关于修改Error.prototype,虽然toJSON()可能没有Error专门为s 定义,但该方法通常仍然针对对象进行标准化(参考:步骤 3)。因此,碰撞或冲突的风险很小。
Though, to still avoid it completely, JSON.stringify()'s replacerparametercan be used instead:
但是,为了仍然完全避免它,可以使用JSON.stringify()'sreplacer参数代替:
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));
回答by laggingreflex
JSON.stringify(err, Object.getOwnPropertyNames(err))
seems to work
似乎工作
[from a comment by /u/ub3rgeek on /r/javascript] and felixfbecker's comment below
[来自 /u/ub3rgeek 对 /r/javascript的评论] 和下面 felixfbecker 的评论
回答by Sanghyun Lee
As no one is talking about the whypart, I'm gonna answer it.
由于没有人在谈论为什么部分,我将回答它。
Why this JSON.stringifyreturns an empty object?
为什么这会JSON.stringify返回一个空对象?
> JSON.stringify(error);
'{}'
Answer
回答
From the document of JSON.stringify(),
从JSON.stringify()的文档中,
For all the other Object instances (including Map, Set, WeakMap and WeakSet), only their enumerable properties will be serialized.
对于所有其他 Object 实例(包括 Map、Set、WeakMap 和 WeakSet),只会序列化它们的可枚举属性。
and Errorobject doesn't have its enumerable properties, that's why it prints an empty object.
并且Errorobject 没有其可枚举属性,这就是它打印空对象的原因。
回答by Bryan Larsen
Modifying Jonathan's great answer to avoid monkey patching:
修改乔纳森的好答案以避免猴子补丁:
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'));
回答by Lukasz Czerwinski
There is a great Node.js package for that: serialize-error.
有一个很棒的 Node.js 包:serialize-error.
It handles well even nested Error objects, what I actually I needed much in my project.
它甚至可以很好地处理嵌套的 Error 对象,这是我在项目中非常需要的。
回答by cheolgook
You can also just redefine those non-enumerable properties to be enumerable.
您也可以将那些不可枚举的属性重新定义为可枚举的。
Object.defineProperty(Error.prototype, 'message', {
configurable: true,
enumerable: true
});
and maybe stackproperty too.
也许还有stack财产。
回答by Joel Malone
We needed to serialise an arbitrary object hierarchy, where the root or any of the nested properties in the hierarchy could be instances of Error.
我们需要序列化任意对象层次结构,其中根或层次结构中的任何嵌套属性都可以是 Error 的实例。
Our solution was to use the replacerparam of JSON.stringify(), e.g.:
我们的解决方案是使用 的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))
回答by Elliott Palermo
None of the answers above seemed to properly serialize properties which are on the prototype of Error (because getOwnPropertyNames()does not include inherited properties). I was also not able to redefine the properties like one of the answers suggested.
上面的答案似乎都没有正确序列化 Error 原型上的属性(因为getOwnPropertyNames()不包括继承的属性)。我也无法像建议的答案之一那样重新定义属性。
This is the solution I came up with - it uses lodash but you could replace lodash with generic versions of those functions.
这是我想出的解决方案 - 它使用 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);
}
Here's the test I did in Chrome:
这是我在 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}}}
回答by Jason
I was working on a JSON format for log appenders and ended up here trying to solve a similar problem. After a while, I realized I could just make Node do the work:
我正在研究日志附加程序的 JSON 格式,并最终在这里尝试解决类似的问题。过了一会儿,我意识到我可以让 Node 来完成这项工作:
const util = require("util");
...
return JSON.stringify(obj, (name, value) => {
if (value instanceof Error) {
return util.format(value);
} else {
return value;
}
}

