JSON.stringify () सरणी प्रोटोटाइप के साथ विचित्रता


89

मैं यह जानने की कोशिश कर रहा हूं कि मेरे जौन सीरीज़िंग में क्या गड़बड़ी हुई है, मेरे ऐप के वर्तमान संस्करण के साथ पुराना और पुराना है और JSON.stringify () के काम करने के तरीके में कुछ आश्चर्यजनक अंतर पा रहा हूँ (json.org से JSON लाइब्रेरी का उपयोग करके) )।

मेरे ऐप के पुराने संस्करण में:

 JSON.stringify({"a":[1,2]})

मुझे यह देता है;

"{\"a\":[1,2]}"

नए संस्करण में,

 JSON.stringify({"a":[1,2]})

मुझे यह देता है;

"{\"a\":\"[1, 2]\"}"

किसी भी विचार को एक ही पुस्तकालय को नए संस्करण में सरणी कोष्ठक के आसपास उद्धरण देने के लिए क्या बदल सकता है?


4
ऐसा लगता है कि यह प्रोटोटाइप लाइब्रेरी के साथ संघर्ष है, जिसे हमने नए संस्करण में पेश किया है। किसी भी विचार कैसे प्रोटोटाइप के तहत एक सरणी युक्त एक json वस्तु को कठोर करने के लिए?
मॉर्गनकोड्स

26
यही कारण है कि लोगों को वैश्विक निर्मित वस्तुओं (जैसा कि प्रोटोटाइप ढांचा करता है) के साथ युद्ध करने से बचना चाहिए
गेरार्डो लीमा

जवाबों:


82

चूंकि JSON.stringify कुछ ब्राउज़रों के साथ हाल ही में शिपिंग किया गया है, इसलिए मैं इसे प्रोटोटाइप के toJSON के बजाय इसका उपयोग करने का सुझाव दूंगा। फिर आप window.JSON && window.JSON.stringify के लिए जांच करेंगे और केवल json.org लाइब्रेरी को अन्यथा ( document.createElement('script')… के माध्यम से ) शामिल करेंगे। असंगतताओं को हल करने के लिए, उपयोग करें:

if(window.Prototype) {
    delete Object.prototype.toJSON;
    delete Array.prototype.toJSON;
    delete Hash.prototype.toJSON;
    delete String.prototype.toJSON;
}

अपने स्वयं के कोड में window.JSON के लिए जाँच करने की कोई आवश्यकता नहीं है - json.org स्क्रिप्ट यह स्वयं करती है
zcrar70

ऐसा हो सकता है, लेकिन तब पूरी स्क्रिप्ट फ़ाइल को लोड करना होगा, भले ही इसकी आवश्यकता न हो।
राफेल श्विकर्ट

11
वास्तव में, इस प्रश्न से निपटने के लिए आवश्यक एकमात्र कथन है: Array.prototype.toJSON
Jean Vincent

1
बहुत बहुत धन्यवाद। जिस कंपनी में मैं अभी काम करता हूं, वह अभी भी हमारे बहुत से कोड में प्रोटोटाइप का उपयोग करती है और अधिक आधुनिक पुस्तकालयों का उपयोग करने के लिए यह एक जीवन रक्षक था, अन्यथा सब कुछ टूटने वाला था।
क्रब

1
मैं DAYS के लिए इस उत्तर को खोज रहा हूं, और दो अलग-अलग SO प्रश्नों को पोस्ट करके यह पता लगाने की कोशिश कर रहा हूं। यह एक संबंधित प्रश्न के रूप में देखा, क्योंकि मैं एक तीसरा टाइप कर रहा था। आपको बहुत - बहुत धन्यवाद!
मैथ्यू हर्बस्ट

78

फ़ंक्शन JSON.stringify () ECMAScript 5 और इसके बाद के संस्करण (पृष्ठ 201 - JSON ऑब्जेक्ट, छद्म कोड पृष्ठ 205) में परिभाषित किया गया है, ऑब्जेक्ट पर उपलब्ध होने पर फ़ंक्शन toJSON () का उपयोग करता है।

चूँकि Prototyp.js (या एक अन्य पुस्तकालय जो आप उपयोग कर रहे हैं) एक Array.prototyp.toJSON () फ़ंक्शन को परिभाषित करता है, सरणियों को पहले Array.prototyp.toJSON () का उपयोग करके स्ट्रिंग में परिवर्तित किया जाता है, फिर JSON.stringify () द्वारा स्ट्रिंग उद्धरण, इसलिए। सरणियों के आसपास गलत अतिरिक्त उद्धरण।

समाधान इसलिए सीधे-सीधे और तुच्छ है (यह राफेल श्विकर्ट के उत्तर का एक सरलीकृत संस्करण है):

delete Array.prototype.toJSON

यह पुस्तकालयों पर निश्चित रूप से साइड इफेक्ट पैदा करता है जो सरणियों के लिए एक toJSON () फ़ंक्शन प्रॉपर्टी पर भरोसा करते हैं। लेकिन मुझे ECMAScript 5 के साथ असंगति को देखते हुए यह एक छोटी असुविधा है।

यह ध्यान दिया जाना चाहिए कि ECMAScript 5 में परिभाषित JSON ऑब्जेक्ट आधुनिक ब्राउज़रों में कुशलता से लागू किया गया है और इसलिए सबसे अच्छा समाधान मानक के अनुरूप है और मौजूदा पुस्तकालयों को संशोधित करना है।


5
यह सरणी का अतिरिक्त उद्धरण के साथ क्या चल रहा है, इसका सबसे संक्षिप्त जवाब है।
tmarthal

15

एक संभावित समाधान जो अन्य प्रोटोटाइप निर्भरता को प्रभावित नहीं करेगा, वह होगा:

var _json_stringify = JSON.stringify;
JSON.stringify = function(value) {
    var _array_tojson = Array.prototype.toJSON;
    delete Array.prototype.toJSON;
    var r=_json_stringify(value);
    Array.prototype.toJSON = _array_tojson;
    return r;
};

यह JSON.stringify के साथ Array toJSON असंगति का ख्याल रखता है और toJSON कार्यक्षमता को भी बरकरार रखता है क्योंकि अन्य प्रोटोटाइप लाइब्रेरी इस पर निर्भर हो सकते हैं।


मैंने एक वेबसाइट में इस स्निपेट का इस्तेमाल किया। इससे समस्याएं पैदा हो रही हैं। यह सरणी की toJSON संपत्ति के अपरिभाषित होने का परिणाम है। उस पर कोई संकेत?
सौरभ

1
कृपया सुनिश्चित करें कि Jray.stringify को फिर से परिभाषित करने के लिए उपरोक्त स्निपेट का उपयोग करने से पहले आपका Array.prototyp.toJSON परिभाषित किया गया है। यह मेरे परीक्षण में ठीक काम करता है।
अक्किशोर

2
में लिपट गया if(typeof Prototype !== 'undefined' && parseFloat(Prototype.Version.substr(0,3)) < 1.7 && typeof Array.prototype.toJSON !== 'undefined')। इसने काम कर दिया।
सौरभ

1
महान। केवल जब तक प्रोटोटाइप 1.7 यह एक मुद्दा है। कृपया आगे
बढ़ें

1
मुद्दा संस्करण के लिए है <1.7
सौरभ

9

थोड़ा और सटीक बनाने के लिए संपादित करें:

कोड की समस्या बिट बिट JSON.org से JSON लाइब्रेरी में है (और ECMAScript 5 के JOS ऑब्जेक्ट के अन्य कार्यान्वयन):

if (value && typeof value === 'object' &&
  typeof value.toJSON === 'function') {
  value = value.toJSON(key);
}

समस्या यह है कि प्रोटोटाइप लाइब्रेरी में एक toJSON विधि शामिल करने के लिए Array फैली हुई है, जिसे JSON ऑब्जेक्ट ऊपर दिए गए कोड में कॉल करेगा। जब JSON ऑब्जेक्ट एरे वैल्यू को हिट करता है तो उसे टॉनसन को उस सरणी पर कॉल करता है, जिसे प्रोटोटाइप में परिभाषित किया गया है, और यह विधि ऐरे का एक स्ट्रिंग संस्करण लौटाती है। इसलिए, सरणी कोष्ठक के आसपास उद्धरण।

यदि आप ऐरे ऑब्जेक्ट से toJSON को हटाते हैं तो JSON लाइब्रेरी को ठीक से काम करना चाहिए। या, सिर्फ JSON लाइब्रेरी का उपयोग करें।


2
यह लाइब्रेरी में एक बग नहीं है, क्योंकि यह सटीक तरीका है कि JSON.stringify () को ECMAScript 5 में परिभाषित किया गया है। समस्या प्रोटोटाइप.जेएस के साथ है और समाधान है: Array.prototyp.toJSON को हटा दें, इसका कुछ पक्ष होगा। प्रोटोटाइप toJSON सीरियलाइज़ेशन के लिए प्रभाव, लेकिन मुझे ये नाबालिगता असंगतता के संबंध में मिली, जो प्रोटोटाइप ECMAScript 5 के साथ है
जीन विंसेंट

प्रोटोटाइप लाइब्रेरी में Object.prototype का विस्तार नहीं किया गया है, लेकिन Array.prototype, हालांकि जावास्क्रिप्ट में टाइपोफ़ सरणी भी "ऑब्जेक्ट" देता है, उनके पास समान "निर्माता" और प्रोटोटाइप नहीं है। आपको जिस समस्या की आवश्यकता है, उसे हल करने के लिए: "Array.prototyp.toJSON हटाएं?"
जीन विंसेंट

@ जीन निष्पक्ष होने के लिए, प्रोटोटाइप ऑब्जेक्ट सहित सभी मूल मूल वस्तुओं का विस्तार करता है। लेकिन ठीक है, मैं आपकी बात फिर से देखता हूं :) मेरे जवाब को बेहतर बनाने में मदद करने के लिए धन्यवाद
बॉब

प्रोटोटाइप ने लंबे समय से "ऑब्जेक्ट.प्रोटोटाइप" का विस्तार रोक दिया है (मुझे याद नहीं है कि कौन सा संस्करण हालांकि) के लिए से बचने के लिए .. मुद्दों में। यह अब केवल नाम के रूप में ऑब्जेक्ट के स्थिर गुणों (जो कि अधिक सुरक्षित है) का विस्तार करता है: api.prototypjs.org/language/Object
जीन विंसेंट

जीन, वास्तव में यह पुस्तकालय में एक बग है। यदि किसी ऑब्जेक्ट में toJSON है, तो उसे कॉल किया जाना चाहिए और इसके परिणाम का उपयोग किया जाना चाहिए, लेकिन इसे उद्धृत नहीं किया जाना चाहिए।
grr

4

मुझे लगता है कि प्रोटोटाइप लोड होने के बाद इसे शामिल करने के लिए एक बेहतर समाधान होगा

JSON = JSON || {};

JSON.stringify = function(value) { return value.toJSON(); };

JSON.parse = JSON.parse || function(jsonsring) { return jsonsring.evalJSON(true); };

यह प्रोटोटाइप फ़ंक्शन मानक JSON.stringify () और JSON.parse () के रूप में उपलब्ध कराता है, लेकिन यह उपलब्ध होने पर देशी JSON.parse () को सुरक्षित रखता है, इसलिए यह पुराने ब्राउज़रों के साथ चीजों को अधिक संगत बनाता है।


JSON.stringify संस्करण काम नहीं करता है यदि 'मान' में पारित एक वस्तु है। आपको इसके बजाय यह करना चाहिए: JSON.stringify = function (value) {return object.toJSON (value); };
अक्किशोर

2

मैं प्रोटोटाइप के साथ धाराप्रवाह नहीं हूं, लेकिन मैंने इसके डॉक्स में यह देखा :

Object.toJSON({"a":[1,2]})

मुझे यकीन नहीं है कि अगर यह एक ही समस्या वर्तमान एन्कोडिंग है, हालांकि होगा।

प्रोटोटाइप के साथ JSON का उपयोग करने के बारे में एक लंबा ट्यूटोरियल भी है ।


2

यह वही कोड है जिसका उपयोग मैंने इस मुद्दे के लिए किया है:

function stringify(object){
      var Prototype = window.Prototype
      if (Prototype && Prototype.Version < '1.7' &&
          Array.prototype.toJSON && Object.toJSON){
              return Object.toJSON(object)
      }
      return JSON.stringify(object)
}

आप जाँचते हैं कि क्या प्रोटोटाइप मौजूद है, तो आप संस्करण की जाँच करें। यदि पुराना संस्करण Object.toJSON का उपयोग करता है (यदि परिभाषित है) अन्य सभी मामलों में JSON.stringify () में आते हैं।


1

यहां बताया गया है कि मैं इससे कैसे निपट रहा हूं।

var methodCallString =  Object.toJSON? Object.toJSON(options.jsonMethodCall) :  JSON.stringify(options.jsonMethodCall);

1

मेरा सहिष्णु समाधान यह जाँचता है कि क्या Array.prototype.toJSON JSON स्ट्रिंग के लिए हानिकारक है और जब संभव हो तो आसपास के कोड को काम करने देने के लिए इसे रखता है:

var dummy = { data: [{hello: 'world'}] }, test = {};

if(Array.prototype.toJSON) {
    try {
        test = JSON.parse(JSON.stringify(dummy));
        if(!test || dummy.data !== test.data) {
            delete Array.prototype.toJSON;
        }
    } catch(e) {
        // there only hope
    }
}

1

जैसा कि लोगों ने बताया है, यह प्रोटोटाइप के कारण है। जेएस - विशेष रूप से संस्करण 1.7 से पहले। मेरे पास भी ऐसी ही स्थिति थी, लेकिन कोड होना चाहिए था जो कि प्रोटोटाइप.जेएस था या नहीं; इसका मतलब यह है कि मैं सिर्फ Array.prototype.toJSON को हटा नहीं सकता क्योंकि मुझे यकीन नहीं है कि इस पर क्या निर्भर करता है। उस स्थिति के लिए यह सबसे अच्छा उपाय है जो मैं लेकर आया हूँ:

function safeToJSON(item){ 
    if ([1,2,3] === JSON.parse(JSON.stringify([1,2,3]))){
        return JSON.stringify(item); //sane behavior
    } else { 
        return item.toJSON(); // Prototype.js nonsense
    }
}

उम्मीद है कि यह किसी की मदद करेगा।


0

यदि आप सब कुछ नहीं मारना चाहते हैं, और एक कोड है जो अधिकांश ब्राउज़रों पर ठीक होगा, तो आप इसे इस तरह से कर सकते हैं:

(function (undefined) { // This is just to limit _json_stringify to this scope and to redefine undefined in case it was
  if (true ||typeof (Prototype) !== 'undefined') {
    // First, ensure we can access the prototype of an object.
    // See http://stackoverflow.com/questions/7662147/how-to-access-object-prototype-in-javascript
    if(typeof (Object.getPrototypeOf) === 'undefined') {
      if(({}).__proto__ === Object.prototype && ([]).__proto__ === Array.prototype) {
        Object.getPrototypeOf = function getPrototypeOf (object) {
          return object.__proto__;
        };
      } else {
        Object.getPrototypeOf = function getPrototypeOf (object) {
          // May break if the constructor has been changed or removed
          return object.constructor ? object.constructor.prototype : undefined;
        }
      }
    }

    var _json_stringify = JSON.stringify; // We save the actual JSON.stringify
    JSON.stringify = function stringify (obj) {
      var obj_prototype = Object.getPrototypeOf(obj),
          old_json = obj_prototype.toJSON, // We save the toJSON of the object
          res = null;
      if (old_json) { // If toJSON exists on the object
        obj_prototype.toJSON = undefined;
      }
      res = _json_stringify.apply(this, arguments);
      if (old_json)
        obj_prototype.toJSON = old_json;
      return res;
    };
  }
}.call(this));

यह जटिल लगता है, लेकिन यह केवल अधिकांश उपयोग के मामलों को संभालने के लिए जटिल है। मुख्य विचार एक तर्क के रूप में पारित वस्तु से JSON.stringifyनिकालने के लिए ओवरराइडिंग है toJSON, फिर पुराने को कॉल करें JSON.stringify, और अंत में इसे पुनर्स्थापित करें।

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