JSON.stringify() 数组与 Prototype.js 的奇怪之处

IT技术 javascript json prototypejs
2021-02-09 16:59:32

我试图找出我的 json 序列化出了什么问题,使用我的应用程序的当前版本和旧版本,我发现 JSON.stringify() 的工作方式有一些令人惊讶的差异(使用来自 json.org 的 JSON 库)。

在我的应用程序的旧版本中:

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

给我这个;

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

在新版本中,

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

给我这个;

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

知道什么可以改变使同一个库在新版本中在数组括号周围加上引号吗?

6个回答

由于 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;
}
可能是这样,但是即使不需要,也必须加载整个脚本文件。
2021-03-15 16:59:32
无需在您自己的代码中检查 window.JSON - json.org 脚本会自行执行此操作
2021-03-20 16:59:32
实际上,处理这个问题所需的唯一语句是:delete Array.prototype.toJSON
2021-03-31 16:59:32
太感谢了。我现在工作的公司目前仍在我们的大部分代码中使用原型,这是使用更多现代库的救星,否则一切都会崩溃。
2021-03-31 16:59:32
我一直在为 DAYS 寻找这个答案,并发布了两个不同的 SO 问题试图弄清楚。当我输入第三个时,将其视为一个相关问题。太感谢了!
2021-04-09 16:59:32

ECMAScript 5 及更高版本(第 201 页 - JSON 对象,伪代码第 205 页)中定义的函数 JSON.stringify()在对象上可用时使用函数 toJSON() 。

因为 Prototype.js(或您正在使用的其他库)定义了一个 Array.prototype.toJSON() 函数,所以首先使用 Array.prototype.toJSON() 将数组转换为字符串,然后使用 JSON.stringify() 引用的字符串,因此数组周围不正确的额外引号。

因此,该解决方案是直接且微不足道的(这是 Raphael Schweikert 答案的简化版本):

delete Array.prototype.toJSON

这当然会对依赖于数组的 toJSON() 函数属性的库产生副作用。但考虑到与 ECMAScript 5 的不兼容,我发现这是一个小小的不便。

必须注意的是,ECMAScript 5 中定义的 JSON 对象在现代浏览器中得到了有效实现,因此最好的解决方案是符合标准并修改现有库。

这是关于数组的额外引用的最简洁的答案。
2021-04-04 16:59:32

一个不会影响其他 Prototype 依赖项的可能解决方案是:

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

这处理了 Array toJSON 与 JSON.stringify 的不兼容问题,并且还保留了 toJSON 功能,因为其他 Prototype 库可能依赖它。

问题是版本 < 1.7
2021-03-14 16:59:32
伟大的。只有在 Prototype 1.7 之前,这是一个问题。请点赞 :)
2021-03-21 16:59:32
在使用上述代码段重新定义 JSON.stringify 之前,请确保您的 Array.prototype.toJSON 已定义。它在我的测试中运行良好。
2021-03-23 16:59:32
我包裹在if(typeof Prototype !== 'undefined' && parseFloat(Prototype.Version.substr(0,3)) < 1.7 && typeof Array.prototype.toJSON !== 'undefined'). 有效。
2021-03-23 16:59:32
我在一个网站上使用了这个片段。它正在引起问题。它导致数组的 toJSON 属性未定义。任何指示?
2021-04-09 16:59:32

编辑以使其更准确:

问题关键的代码位于 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 中定义的数组上调用 toJSON,该方法返回数组的字符串版本。因此,数组括号周围的引号。

如果您从 Array 对象中删除 toJSON,则 JSON 库应该可以正常工作。或者,只需使用 JSON 库。

Prototype 已经停止扩展“Object.prototype”很长时间了(虽然我不记得是哪个版本)以避免 for .. in 问题。它现在只将 Object 的静态属性(更安全)扩展为命名空间:api.prototypejs.org/language/Object
2021-03-14 16:59:32
Jean,实际上这正是图书馆中的一个错误。如果对象具有 toJSON,则必须调用该对象并使用其结果,但不应引用它。
2021-03-22 16:59:32
这不是库中的错误,因为这是在 ECMAScript 5 中定义 JSON.stringify() 的确切方式。问题出在prototype.js 上,解决方案是:删除 Array.prototype.toJSON 这将有一些方面原型 toJSON 序列化的影响,但我发现这些较小的原型与 ECMAScript 5 的不兼容。
2021-03-28 16:59:32
@Jean 公平地说,Prototype 扩展了所有基本本机对象,包括 Object。但是好的,我再次明白您的观点:) 感谢您帮助我更好地回答
2021-03-29 16:59:32
Prototype 库不扩展Object.prototype 而是扩展Array.prototype,虽然JavaScript 中的typeof 数组也返回“object”,但它们并没有相同的“构造函数”和原型。要解决这个问题你需要:“删除Array.prototype.toJSON;”
2021-04-04 16:59:32

我认为更好的解决方案是在加载原型后立即包含它

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.stringify 版本不起作用。你应该这样做: JSON.stringify = function(value) { return Object.toJSON(value); };
2021-04-05 16:59:32