Estoy tratando de averiguar qué salió mal con mi serialización json, tengo la versión actual de mi aplicación con la anterior y encuentro algunas diferencias sorprendentes en la forma en que JSON.stringify () funciona (usando la biblioteca JSON de json.org ).
En la versión anterior de mi aplicación:
JSON.stringify({"a":[1,2]})
me da esto;
"{\"a\":[1,2]}"
en la nueva versión,
JSON.stringify({"a":[1,2]})
me da esto;
"{\"a\":\"[1, 2]\"}"
¿Alguna idea de qué podría haber cambiado para que la misma biblioteca pusiera comillas alrededor de los corchetes de la matriz en la nueva versión?
javascript
json
prototypejs
códigos morgan
fuente
fuente
Respuestas:
Dado que JSON.stringify se ha estado enviando con algunos navegadores últimamente, sugeriría usarlo en lugar del toJSON de Prototype. Luego, verificaría window.JSON && window.JSON.stringify y solo incluiría la biblioteca json.org de lo contrario (a través de
document.createElement('script')
…). Para resolver las incompatibilidades, utilice:if(window.Prototype) { delete Object.prototype.toJSON; delete Array.prototype.toJSON; delete Hash.prototype.toJSON; delete String.prototype.toJSON; }
fuente
La función JSON.stringify () definida en ECMAScript 5 y superior (página 201 - el objeto JSON, pseudocódigo página 205) , usa la función toJSON () cuando está disponible en objetos.
Debido a que Prototype.js (u otra biblioteca que esté usando) define una función Array.prototype.toJSON (), las matrices primero se convierten en cadenas usando Array.prototype.toJSON () y luego la cadena entre comillas JSON.stringify (), de ahí el comillas adicionales incorrectas alrededor de las matrices.
Por lo tanto, la solución es sencilla y trivial (esta es una versión simplificada de la respuesta de Raphael Schweikert):
delete Array.prototype.toJSON
Por supuesto, esto produce efectos secundarios en las bibliotecas que dependen de una propiedad de función toJSON () para matrices. Pero encuentro esto como un inconveniente menor considerando la incompatibilidad con ECMAScript 5.
Cabe señalar que el objeto JSON definido en ECMAScript 5 se implementa de manera eficiente en los navegadores modernos y, por lo tanto, la mejor solución es ajustarse al estándar y modificar las bibliotecas existentes.
fuente
Una posible solución que no afectará a otras dependencias de Prototype sería:
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; };
Esto se encarga de la incompatibilidad de Array toJSON con JSON.stringify y también conserva la funcionalidad de toJSON, ya que otras bibliotecas de Prototype pueden depender de ella.
fuente
if(typeof Prototype !== 'undefined' && parseFloat(Prototype.Version.substr(0,3)) < 1.7 && typeof Array.prototype.toJSON !== 'undefined')
. Funcionó.Edite para hacer un poco más preciso:
El bit de código clave del problema está en la biblioteca JSON de JSON.org (y otras implementaciones del objeto JSON de ECMAScript 5):
if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); }
El problema es que la biblioteca Prototype extiende Array para incluir un método toJSON, que el objeto JSON llamará en el código anterior. Cuando el objeto JSON alcanza el valor de la matriz, llama a toJSON en la matriz que está definida en Prototype, y ese método devuelve una versión de cadena de la matriz. Por lo tanto, las comillas alrededor de los corchetes de la matriz.
Si elimina toJSON del objeto Array, la biblioteca JSON debería funcionar correctamente. O simplemente use la biblioteca JSON.
fuente
Creo que una mejor solución sería incluir esto justo después de que se haya cargado el prototipo
JSON = JSON || {}; JSON.stringify = function(value) { return value.toJSON(); }; JSON.parse = JSON.parse || function(jsonsring) { return jsonsring.evalJSON(true); };
Esto hace que la función prototipo esté disponible como JSON.stringify () y JSON.parse () estándar, pero mantiene el JSON.parse () nativo si está disponible, por lo que esto hace que las cosas sean más compatibles con los navegadores más antiguos.
fuente
No soy tan fluido con Prototype, pero vi esto en sus documentos :
Object.toJSON({"a":[1,2]})
Sin embargo, no estoy seguro de si esto tendría el mismo problema que la codificación actual.
También hay un tutorial más largo sobre el uso de JSON con Prototype.
fuente
Este es el código que usé para el mismo 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) }
Verifica si el prototipo existe, luego verifica la versión. Si la versión anterior usa Object.toJSON (si está definida) en todos los demás casos, recurra a JSON.stringify ()
fuente
Así es como lo estoy afrontando.
var methodCallString = Object.toJSON? Object.toJSON(options.jsonMethodCall) : JSON.stringify(options.jsonMethodCall);
fuente
Mi solución tolerante verifica si Array.prototype.toJSON es dañino para JSON stringify y lo mantiene cuando es posible para que el código circundante funcione como se esperaba:
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 } }
fuente
Como la gente ha señalado, esto se debe a Prototype.js, específicamente versiones anteriores a la 1.7. Tuve una situación similar pero tenía que tener un código que funcionara si Prototype.js estaba allí o no; esto significa que no puedo simplemente eliminar Array.prototype.toJSON ya que no estoy seguro de qué se basa en él. Para esa situación, esta es la mejor solución que se me ocurrió:
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 } }
Ojalá ayude a alguien.
fuente
Si no desea eliminar todo y tiene un código que estaría bien en la mayoría de los navegadores, puede hacerlo de esta manera:
(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));
Esto parece complejo, pero es complejo solo para manejar la mayoría de los casos de uso. La idea principal es primordial
JSON.stringify
para eliminartoJSON
del objeto pasado como argumento, luego llamar al antiguoJSON.stringify
y finalmente restaurarlo.fuente