क्या JSON.stringify का उपयोग करके त्रुटि को स्ट्रिंग करना संभव नहीं है?


330

समस्या का पुन: प्रस्तुत करना

जब मैं वेब सॉकेट का उपयोग करते हुए त्रुटि संदेशों को पास करने की कोशिश कर रहा हूं तो मैं एक समस्या में चल रहा हूं। मैं उस मुद्दे को दोहरा सकता हूं जिसका मैं 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);
    '{}'

समस्या यह है कि मैं एक खाली वस्तु के साथ समाप्त होता हूं।

मैंने क्या कोशिश की है

ब्राउज़र्स

मैंने पहली बार नोड.जेएस छोड़ने और इसे विभिन्न ब्राउज़रों में चलाने की कोशिश की। क्रोम संस्करण 28 मुझे एक ही परिणाम देता है, और दिलचस्प रूप से पर्याप्त है, फ़ायरफ़ॉक्स कम से कम एक प्रयास करता है लेकिन संदेश छोड़ दिया है:

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

पुनरावृत्ति करने वाला कार्य

मैंने तब 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? यदि नहीं, तो यह व्यवहार क्यों होता है?

इसके आस-पास होने के तरीके

  • सरल स्ट्रिंग-आधारित त्रुटि संदेशों के साथ छड़ी करें, या व्यक्तिगत त्रुटि ऑब्जेक्ट बनाएं और मूल त्रुटि ऑब्जेक्ट पर भरोसा न करें।
  • खींच गुण: 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

स्वीकृत उत्तर इस समस्या के लिए समाधान प्रदान करता है।


3
क्या आपने त्रुटि वस्तु में गुणों के लिए संपत्ति के विवरणों की जांच की है?
रे तोल

3
मेरे लिए सवाल 'क्यों' था, और मैंने पाया कि सवाल का जवाब सबसे नीचे था। आपके अपने प्रश्न का उत्तर पोस्ट करने में कुछ भी गलत नहीं है, और आप शायद इस तरह से अधिक विश्वसनीय होंगे। :-)
माइकल स्काइपर

जवाबों:


178

आप एक Error.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"}

का उपयोग कर Object.defineProperty()कहते हैं, toJSONयह बिना एक enumerableसंपत्ति है।


संशोधित करने के बारे में 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));

3
यदि आप .getOwnPropertyNames()इसके बजाय का उपयोग करते हैं .keys(), तो आपको गैर-प्रवर्तनीय गुण मिलेंगे बिना उन्हें मैन्युअल रूप से परिभाषित करने के।

8
Error.prototype के लिए बेहतर नहीं है, जब JavaScrip के भविष्य के संस्करण में Error.prototype वास्तव में एक toJSON फ़ंक्शन है, तो समस्याएँ दे सकता है।
जोस डे जोंग

3
सावधान! यह समाधान मूल नोड मोंगोडब ड्राइवर में त्रुटि से निपटने को तोड़ता है: jira.mongodb.org/browse/NODE-554
सेबस्टियन नोवाक

5
मामले में किसी को भी उनके लिंकर त्रुटियों और नामकरण संघर्ष की ओर ध्यान देता: replacer विकल्प का उपयोग करते हैं, आप एक अलग पैरामीटर नाम का चयन करना चाहिए के लिए keyमें function replaceErrors(key, value)साथ संघर्ष नामकरण से बचने के लिए .forEach(function (key) { .. }); replaceErrors keyपैरामीटर इस जवाब में अप्रयुक्त है।
404 नहीं मिला

2
keyइस उदाहरण की छायांकन , जबकि अनुमति दी गई है, संभावित रूप से भ्रामक है क्योंकि यह संदेह छोड़ देता है कि क्या लेखक बाहरी चर को संदर्भित करना चाहता है या नहीं। propNameआंतरिक लूप के लिए अधिक अभिव्यंजक विकल्प होगा। (BTW, मुझे लगता है कि @ 404NotFound का अर्थ है "लिंटर" (स्थैतिक विश्लेषण उपकरण) "लिंकर" नहीं ) किसी भी मामले में, कस्टम replacerफ़ंक्शन का उपयोग करना इसके लिए एक उत्कृष्ट समाधान है क्योंकि यह समस्या को एक, उपयुक्त स्थान पर हल करता है और देशी को बदल नहीं सकता है / वैश्विक व्यवहार।
IX3

261
JSON.stringify(err, Object.getOwnPropertyNames(err))

काम करने लगता है

[ एक टिप्पणी से / यू / ub3rgeek पर / आर / जावास्क्रिप्ट ] और नीचे felixfbeerer है


57
उत्तर देते हुए,JSON.stringify(err, Object.getOwnPropertyNames(err))
felixfbecker

5
यह एक मूल ExpressJS त्रुटि ऑब्जेक्ट के लिए ठीक काम करता है, लेकिन यह एक Mongoose त्रुटि के साथ काम नहीं करेगा। मानगो त्रुटियों में ValidationErrorप्रकारों के लिए ऑब्जेक्ट्स नेस्टेड हैं । यह errorsप्रकार की मानगो त्रुटि ऑब्जेक्ट में नेस्टेड ऑब्जेक्ट को कड़ा नहीं करेगा ValidationError
स्टीमपॉवर

4
इसका उत्तर होना चाहिए, क्योंकि यह ऐसा करने का सबसे सरल तरीका है।
हुआन

7
@felixfbecker यह केवल प्रॉपर्टी के नाम के लिए एक स्तर गहरा लगता है । यदि आपके पास है var spam = { a: 1, b: { b: 2, b2: 3} };और आप चलते हैं Object.getOwnPropertyNames(spam), तो आपको ["a", "b"]यहाँ मिलेगा - भ्रामक, क्योंकि bऑब्जेक्ट का अपना है b। आपको अपने कठोर कॉल में दोनों मिलेंगे, लेकिन आप चूकspam.b.b2 जाएंगे । यह बुरी बात है।
रफिन

1
@ruffin यह सच है, लेकिन यह वांछनीय भी हो सकता है। मुझे लगता है कि ओपी जो चाहता था वह सिर्फ यह सुनिश्चित करने के लिए किया गया था messageऔर stackJSON में शामिल है।
felixfbecker

74

जैसा कि कोई भी क्यों भाग के बारे में बात नहीं कर रहा है, मैं इसका जवाब देने जा रहा हूं।

यह JSON.stringifyएक खाली वस्तु क्यों देता है?

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

उत्तर

JSON.stringify () के दस्तावेज़ से ,

अन्य सभी ऑब्जेक्ट इंस्टेंसेस (मैप, सेट, वीकएप्स और वीकसेट सहित) के लिए, केवल उनके अनगिनत गुणों को क्रमबद्ध किया जाएगा।

और Errorऑब्जेक्ट के पास अपने असंख्य गुण नहीं हैं, इसीलिए यह एक खाली वस्तु को प्रिंट करता है।


4
अजीब कोई भी परेशान नहीं है। जब तक फिक्स कार्य मुझे लगता है :)
इल्या चेर्नोमोर्डिक

1
इस उत्तर का पहला भाग सही नहीं है। JSON.stringifyइसके replacerपैरामीटर का उपयोग करने का एक तरीका है ।
टॉड चाफी

1
@ToddChaffee यह एक अच्छी बात है। मैंने अपना उत्तर तय कर लिया है। कृपया इसे जांचें और इसे सुधारने के लिए स्वतंत्र महसूस करें। धन्यवाद।
सांग्युन ली

52

बंदर के पेटिंग से बचने के लिए जोनाथन का शानदार जवाब:

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

3
पहली बार मैंने सुना है monkey patching:)
क्रिस प्रिंस

2
@ क्रिसपिन। लेकिन यह आखिरी बार नहीं होगा, जावास्क्रिप्ट में जासूसी! यहाँ बंदर पैचिंग पर विकिपीडिया है , भविष्य के लोगों की जानकारी के लिए। ( जोनाथन के जवाब में , जैसा कि क्रिस समझता है, आप सीधे एक नए फ़ंक्शन को जोड़ रहे हैं toJSON, सीधे Errorप्रोटोटाइप के लिए , जो अक्सर एक महान विचार नहीं है। शायद किसी और के पास पहले से ही है, जो यह जांच करता है, लेकिन फिर आपको नहीं पता कि क्या है वह अन्य संस्करण करता है। या यदि कोई अप्रत्याशित रूप से आपका हो जाता है, या यह मान लेता है कि त्रुटि के प्रोटोटाइप में विशिष्ट गुण हैं, तो चीजें बोर हो सकती हैं।)
रफिन

यह अच्छा है, लेकिन त्रुटि के ढेर को छोड़ देता है (जो कंसोल में दिखाया गया है)। विवरणों के बारे में सुनिश्चित नहीं है, अगर यह Vue-related या क्या है, तो बस इस का उल्लेख करना चाहता था।
दर्शन

23

उसके लिए एक बढ़िया Node.js पैकेज है serialize-error:।

यह अच्छी तरह से नेस्टेड एरर ऑब्जेक्ट्स को भी हैंडल करता है, जो मुझे वास्तव में मेरे प्रोजेक्ट में बहुत जरूरी थे।

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


नहीं, लेकिन ऐसा करने के लिए इसे स्थानांतरित किया जा सकता है। इस टिप्पणी को देखें ।
13

यह सही उत्तर है। सीरियलाइज़िंग त्रुटियां एक तुच्छ समस्या नहीं है, और लाइब्रेरी के लेखक (कई अत्यधिक लोकप्रिय पैकेजों के साथ एक उत्कृष्ट देव) किनारे के मामलों को संभालने के लिए महत्वपूर्ण लंबाई में गए, जैसा कि README में देखा जा सकता है: "कस्टम गुण संरक्षित हैं। गैर-गणना योग्य। संपत्तियों को गैर-गणना योग्य (नाम, संदेश, स्टैक) रखा जाता है। असंख्य गुणों को गणना योग्य रखा जाता है (सभी गुणों को गैर-असंख्य लोगों के अलावा)। परिपत्र संदर्भों को संभाला जाता है। "
डैन डस्केल्सस्कु

9

तुम भी बस उन गैर-enumerable गुणों को फिर से परिभाषित करने के लिए फिर से परिभाषित कर सकते हैं।

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

और शायद stackसंपत्ति भी।


9
जिन वस्तुओं के स्वामी आप नहीं हैं, उन्हें मत बदलिए, यह आपके आवेदन के अन्य भागों और सौभाग्य को तोड़ सकती है।
1948 को शाम

7

हमें एक मनमानी वस्तु पदानुक्रम को क्रमबद्ध करने की आवश्यकता थी, जहां पदानुक्रम में जड़ या किसी भी नेस्टेड गुण त्रुटि के उदाहरण हो सकते हैं।

हमारे समाधान का उपयोग करने के लिए था 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))


5

उपरोक्त में से कोई भी उत्तर गुणों को ठीक से अनुक्रमित करने के लिए प्रतीत नहीं हुआ, जो त्रुटि के प्रोटोटाइप पर है (क्योंकि getOwnPropertyNames()इसमें विरासत में मिला संपत्ति शामिल नहीं है)। मैं भी सुझाए गए उत्तरों में से एक की तरह गुणों को फिर से परिभाषित करने में सक्षम नहीं था।

यह वह समाधान है जिसके साथ मैं आया था - यह लॉश का उपयोग करता है लेकिन आप उन कार्यों के जेनेरिक संस्करणों के साथ लैश को बदल सकते हैं।

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

यह परीक्षण मैंने क्रोम में किया था:

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

2

मैं लॉग अपेंडर्स के लिए JSON फॉर्मेट पर काम कर रहा था और एक समान समस्या को हल करने की कोशिश कर रहा था। थोड़ी देर के बाद, मुझे एहसास हुआ कि मैं सिर्फ काम कर सकता हूँ:

const util = require("util");
...
return JSON.stringify(obj, (name, value) => {
    if (value instanceof Error) {
        return util.format(value);
    } else {
        return value;
    }
}

1
यह होना चाहिए instanceofऔर नहीं भी instanceOf
lakshman.pasala
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.