Bizarrezza dell’array JSON.stringify () con Prototype.js

Sto cercando di capire cosa c’è che non va nella mia serializzazione json, ho la versione attuale della mia app con la vecchia e sto trovando alcune sorprendenti differenze nel modo in cui funziona JSON.stringify () (Usando la libreria JSON da json.org ).

Nella vecchia versione della mia app:

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

mi dà questo;

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

nella nuova versione,

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

mi dà questo;

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

Qualche idea su cosa potrebbe essere cambiato per fare in modo che la stessa libreria inserisse virgolette attorno alle parentesi dell’array nella nuova versione?

Dal momento che JSON.stringify è stato distribuito con alcuni browser ultimamente, suggerirei di utilizzarlo al posto di Prototype toJSON. Dovresti quindi cercare window.JSON && window.JSON.stringify e includere solo la libreria json.org altrimenti (tramite document.createElement('script') …). Per risolvere le incompatibilità, utilizzare:

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

La funzione JSON.stringify () definita in ECMAScript 5 e successivi (Pagina 201 – Oggetto JSON, pseudo-codice Pagina 205) , usa la funzione toJSON () quando disponibile sugli oggetti.

Poiché Prototype.js (o un’altra libreria che si sta utilizzando) definisce una funzione Array.prototype.toJSON (), gli array vengono prima convertiti in stringhe utilizzando Array.prototype.toJSON (), quindi stringa quotata da JSON.stringify (), quindi citazioni extra errate attorno agli array.

La soluzione è quindi semplice e banale (questa è una versione semplificata della risposta di Raphael Schweikert):

 delete Array.prototype.toJSON 

Ciò produce ovviamente effetti collaterali sulle librerie che si basano su una funzione di funzione toJSON () per gli array. Ma trovo questo un piccolo inconveniente considerando l’incompatibilità con ECMAScript 5.

È necessario notare che l’object JSON definito in ECMAScript 5 viene implementato in modo efficiente nei browser moderni e pertanto la soluzione migliore è quella di conformarsi allo standard e modificare le librerie esistenti.

Una ansible soluzione che non influirà su altre dipendenze del Prototipo sarebbe:

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

Questo si prende cura della incompatibilità Array toJSON con JSON.stringify e mantiene anche la funzionalità di JSON come altre librerie di Prototype possono dipendere da esso.

Modifica per rendere un po ‘più preciso:

Il bit chiave di codice del problema si trova nella libreria JSON di JSON.org (e altre implementazioni dell’object JSON di ECMAScript 5):

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

Il problema è che la libreria Prototype estende Array per includere un metodo toJSON, che l’object JSON chiamerà nel codice sopra. Quando l’object JSON colpisce il valore dell’array, chiama a JSON sull’array definito in Prototype e tale metodo restituisce una versione stringa dell’array. Quindi, le virgolette attorno alle parentesi dell’array.

Se si elimina toJSON dall’object Array, la libreria JSON dovrebbe funzionare correttamente. Oppure, usa semplicemente la libreria JSON.

Penso che una soluzione migliore sarebbe includerla subito dopo il caricamento del prototipo

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

Ciò rende la funzione prototipo disponibile come standard JSON.stringify () e JSON.parse (), ma mantiene il nativo JSON.parse () se è disponibile, quindi questo rende le cose più compatibili con i browser più vecchi.

Non sono così fluente con Prototype, ma ho visto questo nei suoi documenti :

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

Non sono sicuro se questo avrebbe lo stesso problema della codifica attuale, però.

C’è anche un tutorial più lungo sull’uso di JSON con Prototype.

Questo è il codice che ho usato per lo stesso problema:

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

Controlli se esiste Prototype, quindi controlli la versione. Se la versione precedente utilizza Object.toJSON (se è stato definito) in tutti gli altri casi, eseguire il fallback su JSON.stringify ()

Ecco come ho a che fare con esso.

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

La mia soluzione tollerante controlla se Array.prototype.toJSON è dannoso per JSON stringify e lo mantiene quando ansible per far funzionare il codice circostante come previsto:

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

Come hanno sottolineato le persone, questo è dovuto a Prototype.js – in particolare versioni precedenti alla 1.7. Avevo una situazione simile ma dovevo avere il codice che funzionava se Prototype.js era lì o no; questo significa che non posso semplicemente cancellare Array.prototype.toJSON perché non sono sicuro di cosa si basi su di esso. Per quella situazione questa è la soluzione migliore che ho trovato:

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

Spero che possa aiutare qualcuno.

Se non vuoi uccidere tutto e avere un codice adatto alla maggior parte dei browser, puoi farlo in questo modo:

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

Questo sembra complesso, ma è complesso solo per gestire la maggior parte dei casi d’uso. L’idea principale è quella di sovrascrivere JSON.stringify per rimuovere toJSON dall’object passato come argomento, quindi chiamare il vecchio JSON.stringify e infine ripristinarlo.