Javascript stringify()数组与Prototype.js的奇异性

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序列化出了什么问题,将我的应用程序的当前版本与旧版本进行了比较,并发现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\":\"[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)
}