Javascript stringify()数组与Prototype.js的奇异性
我试图找出我的json序列化出了什么问题,将我的应用程序的当前版本与旧版本进行了比较,并发现json.stringify()的工作方式(使用json.org中的json库)存在一些令人惊讶的差异 在我的应用程序的旧版本中:Javascript stringify()数组与Prototype.js的奇异性,javascript,json,prototypejs,Javascript,Json,Prototypejs,我试图找出我的json序列化出了什么问题,将我的应用程序的当前版本与旧版本进行了比较,并发现json.stringify()的工作方式(使用json.org中的json库)存在一些令人惊讶的差异 在我的应用程序的旧版本中: JSON.stringify({"a":[1,2]}) JSON.stringify({"a":[1,2]}) 给我这个 "{\"a\":[1,2]}" "{\"a\":\"[1, 2]\"}" 在新版本中 给我这个 "{\"a\":[1,2]}" "{\"a\
JSON.stringify({"a":[1,2]})
JSON.stringify({"a":[1,2]})
给我这个
"{\"a\":[1,2]}"
"{\"a\":\"[1, 2]\"}"
在新版本中
给我这个
"{\"a\":[1,2]}"
"{\"a\":\"[1, 2]\"}"
你知道在新版本中,为了让同一个库在数组括号中加上引号,会发生什么变化吗?下面是我的处理方法
var methodCallString = Object.toJSON? Object.toJSON(options.jsonMethodCall) : JSON.stringify(options.jsonMethodCall);
我对原型不太熟悉,但我从它的: 不过,我不确定这是否会与当前编码存在相同的问题
在Prototype中使用JSON还有一个较长的问题。我认为更好的解决方案是在Prototype加载后立即包含此内容
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.org中的JSON库(以及ECMAScript 5的JSON对象的其他实现): 问题是原型库扩展了数组以包含一个toJSON方法,JSON对象将在上面的代码中调用该方法。当JSON对象命中数组值时,它调用原型中定义的数组上的toJSON,该方法返回数组的字符串版本。因此,数组方括号中的引号是正确的
如果从数组对象中删除toJSON,JSON库应该可以正常工作。或者,只需使用JSON库。由于JSON.stringify最近已随一些浏览器提供,我建议使用它而不是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;
}
中定义的函数JSON.stringify()在对象上可用时使用函数toJSON() 因为Prototype.js(或您正在使用的另一个库)定义了Array.Prototype.toJSON()函数,所以首先使用Array.Prototype.toJSON()将数组转换为字符串,然后使用JSON.stringify()引用字符串,因此数组周围的额外引号不正确 因此,解决方案简单明了(这是Raphael Schweikert答案的简化版本): 这当然会对依赖于数组的toJSON()函数属性的库产生副作用。但考虑到与ECMAScript 5的不兼容性,我觉得这是一个小小的不便
必须注意的是,ECMAScript 5中定义的JSON对象在现代浏览器中得到了有效的实现,因此最好的解决方案是遵守标准并修改现有库。我的容错解决方案检查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
}
}
正如人们指出的,这是由于Prototype.js,特别是1.7之前的版本。我也遇到过类似的情况,但无论Prototype.js是否存在,我都必须有运行的代码;这意味着我不能仅仅删除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
}
}
希望它能帮助某些人。一个不会影响其他原型依赖关系的可能解决方案是:
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;
};
这解决了数组toJSON与JSON.stringify的不兼容问题,还保留了toJSON的功能,因为其他原型库可能依赖于它。如果您不想杀死所有东西,并且有一个在大多数浏览器上都可以使用的代码,您可以这样做:
(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
,最后将其还原。这是我在同一问题中使用的代码:
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)
}
函数字符串化(对象){
var Prototype=window.Prototype
if(原型和原型版本<'1.7'&&
Array.prototype.toJSON&&Object.toJSON){
return Object.toJSON(对象)
}
返回JSON.stringify(对象)
}
检查原型是否存在,然后检查版本。如果旧版本在所有其他情况下都使用Object.toJSON(如果已定义),则回退到JSON。stringify()看起来与我们在新版本中引入的原型库有冲突。你知道如何在Prototype下对包含数组的json对象进行字符串化吗?这就是为什么人们应该避免使用全局内置对象(正如Prototype框架所做的那样)无需在自己的代码中检查window.json-json.org脚本本身就是这样做的,但是,即使不需要,也必须加载整个脚本文件。实际上,处理这个问题所需的唯一语句是:delete Array.prototype.tojson谢谢你。我现在工作的公司目前在我们的大部分代码中仍然使用原型,这是使用更现代的库的一个救命稻草,否则一切都会崩溃。我搜索这个答案已经好几天了,并发布了两个不同的问题,试图找出答案。在我输入第三个问题时,我将此视为一个相关问题。非常感谢你!这不是库中的错误,因为这正是ECMAScript 5中定义JSON.stringify()的方式。prototype.js存在问题,解决方案是:删除Array.prototype.toJSON这将对prototype-toJSON序列化产生一些副作用,但我发现这些副作用与prototype与ECMAScript 5的不兼容有关。prototype库
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)
}