使用Prototype.js JSON.stringify()数组奇怪
我试图找出什么是我的JSON序列化出了问题,具有我的应用程序的旧版本和当前版本JSON.stringify()工作方式(使用json.org JSON库)。
在我的应用程序的旧版本中:
JSON.stringify({"a":[1,2]})
给我这个;
"{\"a\":[1,2]}"
在新版本中,
JSON.stringify({"a":[1,2]})
给我这个;
"{\"a\":\"[1, 2]\"}"
任何想法什么可以改变,使相同的库放在新版本的数组括号周围引号?
由于JSON.stringify最近一直在使用一些浏览器,我build议使用它而不是Prototype的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; }
在ECMAScript 5及更高版本中定义的函数JSON.stringify(),在对象上可用时使用函数toJSON()。
因为Prototype.js(或者你正在使用的另一个库)定义了一个Array.prototype.toJSON()函数,所以首先使用Array.prototype.toJSON()将数组转换为string,然后使用JSON.stringify()引用string,数组周围不正确的额外引号。
因此,解决scheme是简单直接的(这是Raphael Schweikert的答案的简化版本):
delete Array.prototype.toJSON
这对依赖于数组的toJSON()函数属性的库产生了一些副作用。 但考虑到与ECMAScript 5不兼容,我觉得这是一个小小的麻烦。
必须注意的是,在ECMAScript 5中定义的JSON对象在现代浏览器中被高效地实现,因此最好的解决scheme是符合标准并修改现有的库。
不会影响其他Prototype依赖关系的可能的解决scheme是:
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数组toJSON不兼容,并保留到JSONfunction,因为其他Prototype库可能依赖于它。
编辑更精确一点:
问题关键的代码位于JSON.org的JSON库(以及ECMAScript 5的JSON对象的其他实现)中:
if(value && typeof value ==='object'&& typeof value.toJSON ==='function'){ value = value.toJSON(key); }
问题是Prototype库扩展了Array以包含一个toJSON方法,JSON对象将在上面的代码中调用。 当JSON对象命中数组值时,它将在Prototype中定义的数组上调用JSON,并且该方法返回数组的string版本。 因此,数组括号内的引号。
如果从Array对象中删除toJSON,则JSON库应该正常工作。 或者,只需使用JSON库。
我认为一个更好的解决scheme就是在原型加载之后join
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(),所以这使得事情与旧版浏览器更加兼容。
我不太熟悉Prototype,但是我在文档中看到了这个:
Object.toJSON({"a":[1,2]})
我不知道这是否会有相同的问题目前的编码了,但。
还有一个关于在Prototype中使用JSON的更长的教程 。
这是我用于相同的问题的代码:
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()
我正在处理这个问题。
var methodCallString = Object.toJSON? Object.toJSON(options.jsonMethodCall) : JSON.stringify(options.jsonMethodCall);
我宽容的解决scheme检查Array.prototype.toJSON是否对JSON stringify有害,并在可能时让它保持周围的代码正常工作:
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 } }
正如人们已经指出的,这是由于Prototype.js – 特别是1.7之前的版本。 我也有类似的情况,但不pipePrototype.js是否存在,都必须有代码。 这意味着我不能只是删除Array.prototype.toJSON,因为我不知道什么依赖它。 对于这种情况,这是我提出的最好的解决scheme:
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 } }
希望它会帮助别人。
如果你不想杀死所有的东西,并且在大多数浏览器上有一个可以使用的代码,你可以这样做:
(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
从作为parameter passing的对象中删除toJSON
,然后调用旧的JSON.stringify
,最后恢复它。