JSON.stringify () dizisi Prototype.js ile tuhaflık


89

Json serileştirme işlemimde neyin yanlış gittiğini anlamaya çalışıyorum, uygulamamın mevcut sürümü ile eski sürümüne sahibim ve JSON.stringify () çalışma biçiminde bazı şaşırtıcı farklılıklar buluyorum (json.org'dan JSON kitaplığını kullanarak) ).

Uygulamamın eski sürümünde:

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

bana bunu verir;

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

yeni versiyonda

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

bana bunu verir;

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

Yeni sürümde aynı kitaplığın dizi parantezlerinin etrafına tırnak işareti koyması için neyin değişmiş olabileceği hakkında bir fikriniz var mı?


4
Yeni sürümde sunduğumuz Prototip kitaplığıyla bir çelişki var gibi görünüyor. Prototip altında bir dizi içeren bir json nesnesinin nasıl dizgeleştirileceğine dair bir fikriniz var mı?
morgancodes

26
bu yüzden insanlar küresel yerleşik nesnelerle karıştırmaktan kaçınmalı (prototip çerçevesinin yaptığı gibi)
Gerardo Lima

Yanıtlar:


82

JSON.stringify, son zamanlarda bazı tarayıcılarla birlikte gönderildiği için, Prototype'ın toJSON'u yerine onu kullanmanızı öneririm. Daha sonra window.JSON && window.JSON.stringify'ı kontrol edersiniz ve aksi takdirde yalnızca json.org kitaplığını dahil edersiniz ( document.createElement('script')… aracılığıyla ). Uyumsuzlukları çözmek için şunu kullanın:

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

Kendi kodunuzda window.JSON'u kontrol etmenize gerek yok - json.org betiği bunu kendisi yapıyor
zcrar70

Öyle olabilir, ancak daha sonra ihtiyaç duyulmasa bile tüm komut dosyası dosyasının yüklenmesi gerekir.
Raphael Schweikert

11
Aslında, soruyla ilgilenmek için gereken tek ifade şudur: sil Array.prototype.toJSON
Jean Vincent

1
Çok teşekkür ederim. Şu anda çalıştığım şirket şu anda prototipi kodumuzun çoğunda kullanıyor ve bu daha modern kitaplıkları kullanmak için hayat kurtarıcıydı, aksi takdirde her şey bozulacaktı.
krob

1
DAYS için bu cevabı arıyordum ve anlamaya çalışan iki farklı SO sorusu gönderdim. Üçüncüyü yazarken bunu alakalı bir soru olarak gördüm. Çok teşekkür ederim!
Matthew Herbst

80

ECMAScript 5 ve üzerinde (Sayfa 201 - JSON Nesnesi, sözde kod Sayfa 205 ) tanımlanan JSON.stringify () işlevi, nesnelerde mevcut olduğunda toJSON () işlevini kullanır.

Prototype.js (veya kullandığınız başka bir kitaplık) bir Array.prototype.toJSON () işlevini tanımladığından, diziler önce Array.prototype.toJSON () kullanılarak dizelere dönüştürülür, ardından JSON.stringify () tarafından alıntılanan dize, dolayısıyla diziler etrafında yanlış ekstra alıntılar.

Dolayısıyla çözüm basit ve önemsizdir (bu, Raphael Schweikert'in cevabının basitleştirilmiş bir versiyonudur):

delete Array.prototype.toJSON

Bu, elbette diziler için bir toJSON () işlevi özelliğine dayanan kitaplıklar üzerinde yan etkiler yaratır. Ancak ECMAScript 5 ile uyumsuzluk göz önüne alındığında bunu küçük bir rahatsızlık buluyorum.

ECMAScript 5'te tanımlanan JSON Nesnesinin modern tarayıcılarda verimli bir şekilde uygulandığı ve bu nedenle en iyi çözümün standarda uymak ve mevcut kitaplıkları değiştirmek olduğu unutulmamalıdır.


5
Bu, dizinin fazladan alıntılanmasıyla neler olup bittiğinin en kısa cevabıdır.
tmarthal

15

Diğer Prototip bağımlılıklarını etkilemeyecek olası bir çözüm şöyle olacaktır:

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

Bu, JSON.stringify ile Array toJSON uyumsuzluğunu giderir ve ayrıca diğer Prototip kitaplıklarının buna bağlı olabileceği için toJSON işlevselliğini korur.


Bu pasajı bir web sitesinde kullandım. Sorunlara neden oluyor. Dizinin toJSON özelliğinin tanımsız olmasıyla sonuçlanır. Bununla ilgili herhangi bir işaret var mı?
Sourabh

1
JSON.stringify'ı yeniden tanımlamak için yukarıdaki pasajı kullanmadan önce Array.prototype.toJSON'unuzun tanımlandığından emin olun. Benim testimde iyi çalışıyor.
akkishore

2
Ben sarıldım if(typeof Prototype !== 'undefined' && parseFloat(Prototype.Version.substr(0,3)) < 1.7 && typeof Array.prototype.toJSON !== 'undefined'). İşe yaradı.
Sourabh

1
Harika. Sadece Prototip 1.7'ye kadar bu bir sorundur. Lütfen olumlu oy :)
akkishore

1
Sorun <1.7
Sourabh

9

Biraz daha doğru hale getirmek için düzenleyin:

Kodun sorun anahtarı biti, JSON.org'daki JSON kitaplığındadır (ve ECMAScript 5'in JSON nesnesinin diğer uygulamaları):

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

Sorun, Prototip kitaplığının Array'i JSON nesnesinin yukarıdaki kodda çağıracağı bir toJSON yöntemini içerecek şekilde genişletmesidir. JSON nesnesi dizi değerine ulaştığında, Prototype'da tanımlanan dizi üzerinde toJSON'u çağırır ve bu yöntem dizinin bir dize sürümünü döndürür. Dolayısıyla, dizi parantezlerinin etrafındaki tırnaklar.

ToJSON öğesini Array nesnesinden silerseniz, JSON kitaplığının düzgün çalışması gerekir. Veya sadece JSON kitaplığını kullanın.


2
Bu, kütüphanede bir hata değildir, çünkü JSON.stringify () 'nın ECMAScript 5'te tanımlanma şekli tam olarak budur. Sorun prototype.js ile ilgilidir ve çözüm şudur: delete Array.prototype.toJSON Bunun bir tarafı olacaktır prototip toJSON serileştirmesi için etkiler, ancak prototipin ECMAScript 5 ile uyuşmazlığı açısından bunları küçük buldum.
Jean Vincent

Prototip kitaplığı Object.prototype yerine Array.prototype'ı genişletmez, ancak JavaScript'teki typeof array "object" döndürse de, aynı "yapıcı" ve prototipe sahip değildirler. Sorunu çözmek için şunları yapmanız gerekir: "Array.prototype.toJSON;"
Jean Vincent

@Jean Adil olmak gerekirse, Prototype, Object dahil tüm temel yerel nesneleri genişletir. Ama tamam, amacınızı tekrar anlıyorum :) Cevabımın daha iyi olmasına yardımcı olduğunuz için teşekkürler
Bob

Prototip, uzun bir süredir "Object.prototype" 'i genişletmeyi durdurdu (hangi sürümü hatırlamıyorum) sorunlarda for ..'den kaçınmak için. Artık yalnızca Object'in statik özelliklerini (çok daha güvenli) bir ad alanı olarak genişletiyor
Jean Vincent

Jean, aslında tam olarak kütüphanede bir böcek. Bir nesne toJSON'a sahipse, bu çağrılmalı ve sonucu kullanılmalıdır, ancak alıntı yapılmamalıdır.
grr

4

Sanırım prototip yüklendikten hemen sonra bunu dahil etmek daha iyi bir çözüm olacaktır.

JSON = JSON || {};

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

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

Bu, prototip işlevini standart JSON.stringify () ve JSON.parse () olarak kullanılabilir hale getirir, ancak mevcutsa yerel JSON.parse () öğesini korur, böylece bu, işleri eski tarayıcılarla daha uyumlu hale getirir.


JSON.stringify sürümü, iletilen 'değer' bir Nesne ise çalışmaz. Bunun yerine şunu yapmalısınız: JSON.stringify = function (değer) {return Object.toJSON (değer); };
akkishore

2

Prototype konusunda o kadar akıcı değilim, ancak bunu belgelerinde gördüm :

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

Yine de, bunun mevcut kodlamanın sahip olduğu aynı sorunu yaşayıp yaşamayacağından emin değilim.

JSON'u Prototype ile kullanma hakkında daha uzun bir eğitim de var.


2

Aynı sorun için kullandığım kod bu:

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

Prototip'in var olup olmadığını kontrol edersiniz, ardından sürümü kontrol edersiniz. Eski sürüm Object.toJSON (tanımlanmışsa) kullanıyorsa diğer tüm durumlarda JSON.stringify ()


1

İşte bununla nasıl başa çıktığım.

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

1

Toleranslı çözümüm Array.prototype.toJSON'un JSON stringify için zararlı olup olmadığını kontrol ediyor ve mümkün olduğunda çevreleyen kodun beklendiği gibi çalışmasına izin vermeyi sürdürüyor:

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

İnsanların belirttiği gibi, bunun nedeni Prototype.js - özellikle 1.7'den önceki sürümler. Benzer bir durum yaşadım, ancak Prototype.js orada olsa da olmasa da çalışan bir koda sahip olmam gerekiyordu; bu, neyin dayandığından emin olmadığım için Array.prototype.toJSON'u silemeyeceğim anlamına geliyor. Bu durum için bulduğum en iyi çözüm bu:

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

Umarım birine yardımcı olur.


0

Her şeyi öldürmek istemiyorsanız ve çoğu tarayıcıda sorun olmayacak bir kodunuz varsa, bunu şu şekilde yapabilirsiniz:

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

Bu karmaşık görünebilir, ancak bu yalnızca çoğu kullanım durumunu ele almak için karmaşıktır. Ana fikir, argüman olarak iletilen nesneden JSON.stringifykaldırmayı toJSON, ardından eskiyi çağırmayı JSON.stringifyve en sonunda onu geri yüklemeyi geçersiz kılmaktır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.