Is it not possible to stringify an Error using JSON.stringify?

后端 未结 11 2079
心在旅途
心在旅途 2020-11-22 07:54

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 J

相关标签:
11条回答
  • 2020-11-22 08:22
    JSON.stringify(err, Object.getOwnPropertyNames(err))
    

    seems to work

    [from a comment by /u/ub3rgeek on /r/javascript] and felixfbecker's comment below

    0 讨论(0)
  • 2020-11-22 08:26

    As no one is talking about the why part, I'm gonna answer it.

    Why this JSON.stringify returns an empty object?

    > JSON.stringify(error);
    '{}'
    

    Answer

    From the document of JSON.stringify(),

    For all the other Object instances (including Map, Set, WeakMap and WeakSet), only their enumerable properties will be serialized.

    and Error object doesn't have its enumerable properties, that's why it prints an empty object.

    0 讨论(0)
  • 2020-11-22 08:27

    You can also just redefine those non-enumerable properties to be enumerable.

    Object.defineProperty(Error.prototype, 'message', {
        configurable: true,
        enumerable: true
    });
    

    and maybe stack property too.

    0 讨论(0)
  • 2020-11-22 08:28

    You can define a Error.prototype.toJSON to retrieve a plain Object representing the 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 toJSON without it being an enumerable property itself.


    Regarding modifying Error.prototype, while toJSON() may not be defined for Errors specifically, the method is still standardized for objects in general (ref: step 3). So, the risk of collisions or conflicts is minimal.

    Though, to still avoid it completely, JSON.stringify()'s replacer parameter can be used instead:

    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));
    
    0 讨论(0)
  • 2020-11-22 08:31

    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.

    Our solution was to use the replacer param of JSON.stringify(), e.g.:

    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))

    0 讨论(0)
  • 2020-11-22 08:33

    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'));
    
    0 讨论(0)
提交回复
热议问题