javascript - 파싱 - JSON.stringify를 사용하여 오류를 문자열화할 수 있습니까?




자바스크립트 파싱 (5)

문제 재현

웹 소켓을 사용하여 오류 메시지를 전달하려고 할 때 문제가 발생합니다. 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를 떠나 다양한 브라우저에서 실행하려고 시도했다. 크롬 버전 28은 나에게 똑같은 결과를주고 흥미롭게도 파이어 폭스는 최소한 시도를하지만 메시지를 남기지 않았다.

>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}

대용품 기능

나는 그때 Error.prototype 을 보았다. 프로토 타입에 toStringtoSource 와 같은 메서드가 포함되어 있음을 보여줍니다. 함수를 문자열로 변환 할 수 없다는 것을 알고, 모든 함수를 제거하기 위해 JSON.stringify를 호출 할 때 replacer 함수를 포함했지만 그 역시 이상한 동작이 있음을 깨달았습니다.

var error = new Error('simple error message');
JSON.stringify(error, function(key, value) {
    console.log(key === ''); // true (?)
    console.log(value === error); // true (?)
});

그것은 정상적으로 개체를 통해 반복하지 않는 것, 따라서 나는 열쇠가 함수인지 확인하고 그것을 무시할 수 없습니다.

질문

JSON.stringify 를 사용하여 원시 오류 메시지를 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 }

Key : enumerable: false .

수락 된 대답은이 문제에 대한 해결 방법을 제공합니다.


원숭이 패치를 피하기 위해 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'));

위의 대답 중 아무 것도 getOwnPropertyNames() 가 상속 된 속성을 포함하지 않기 때문에 오류의 프로토 타입에있는 속성을 제대로 serialize하는 것으로 보지 않았습니다. 나는 또한 대답 중 하나와 같은 속성을 재정의 할 수 없었다.

이것은 내가 생각해 낸 해결책입니다. 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}}}  

이를 위해 Node.js 패키지가 있습니다 : serialize-error .

그것은 심지어 실제로 내 프로젝트에서 많이 필요한 중첩 된 Error 객체를 처리합니다.

https://www.npmjs.com/package/serialize-error


Error.prototype.toJSON 을 정의하여 Error.prototype.toJSON 를 나타내는 일반 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() 사용하면 enumerable 속성 자체가 아닌 toJSON 추가됩니다.

Error.prototype 수정하는 Error.prototype 관련하여 toJSON()Error 대해 구체적으로 정의되지는 않지만이 방법은 일반적으로 객체에 대해 표준화 되어 있습니다 (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));





error-handling