是否可以使用 JSON.stringify 字符串化错误?

2022-08-29 23:07:52

重现问题

我在尝试使用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"}

替换器功能

然后我查看了 Error.prototype。它表明原型包含 toStringtoSource 等方法。知道函数不能被字符串化,我在调用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托尔在评论中建议我看一下属性描述符。现在很清楚为什么它不起作用:

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

似乎有效

[来自 /u/ub3rgeek 对 /r/javascript 的评论]和felixfbecker在下面的评论


答案 2

您可以定义 一个来检索表示以下内容的平原:Error.prototype.toJSONObjectError

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() 添加,但它不会是属性本身。toJSONenumerable


关于修改,虽然可能没有专门为s定义,但该方法对于一般的对象仍然是标准化的(参考:步骤3)。因此,碰撞或冲突的风险是最小的。Error.prototypetoJSON()Error

但是,为了完全避免这种情况,可以使用JSON.stringify()替换器参数来代替:

function replaceErrors(key, value) {
    if (value instanceof Error) {
        var error = {};

        Object.getOwnPropertyNames(value).forEach(function (propName) {
            error[propName] = value[propName];
        });

        return error;
    }

    return value;
}

var error = new Error('testing');
error.detail = 'foo bar';

console.log(JSON.stringify(error, replaceErrors));