JSON.stringify() array bizarreness with Prototype.

2019-01-02 17:27发布

I'm trying to figure out what's gone wrong with my json serializing, have the current version of my app with and old one and am finding some surprising differences in the way JSON.stringify() works (Using the JSON library from json.org).

In the old version of my app:

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

gives me this;

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

in the new version,

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

gives me this;

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

any idea what could have changed to make the same library put quotes around the array brackets in the new version?

11条回答
初与友歌
2楼-- · 2019-01-02 17:59

The function JSON.stringify() defined in ECMAScript 5 and above (Page 201 - the JSON Object, pseudo-code Page 205), uses the function toJSON() when available on objects.

Because Prototype.js (or another library that you are using) defines an Array.prototype.toJSON() function, arrays are first converted to strings using Array.prototype.toJSON() then string quoted by JSON.stringify(), hence the incorrect extra quotes around the arrays.

The solution is therefore straight-forward and trivial (this is a simplified version of Raphael Schweikert's answer):

delete Array.prototype.toJSON

This produces of course side effects on libraries that rely on a toJSON() function property for arrays. But I find this a minor inconvenience considering the incompatibility with ECMAScript 5.

It must be noted that the JSON Object defined in ECMAScript 5 is efficiently implemented in modern browsers and therefore the best solution is to conform to the standard and modify existing libraries.

查看更多
只靠听说
3楼-- · 2019-01-02 18:01

A possible solution which will not affect other Prototype dependencies would be:

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

This takes care of the Array toJSON incompatibility with JSON.stringify and also retains toJSON functionality as other Prototype libraries may depend on it.

查看更多
弹指情弦暗扣
4楼-- · 2019-01-02 18:01

I think a better solution would be to include this just after prototype has been loaded

JSON = JSON || {};

JSON.stringify = function(value) { return value.toJSON(); };

JSON.parse = JSON.parse || function(jsonsring) { return jsonsring.evalJSON(true); };

This makes the prototype function available as the standard JSON.stringify() and JSON.parse(), but keeps the native JSON.parse() if it is available, so this makes things more compatible with older browsers.

查看更多
谁念西风独自凉
5楼-- · 2019-01-02 18:05

I'm not that fluent with Prototype, but I saw this in its docs:

Object.toJSON({"a":[1,2]})

I'm not sure if this would have the same problem the current encoding has, though.

There's also a longer tutorial about using JSON with Prototype.

查看更多
若你有天会懂
6楼-- · 2019-01-02 18:06

This is the code I used for the same issue:

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

You check if Prototype exists, then you check the version. If old version use Object.toJSON (if is defined) in all other cases fallback to JSON.stringify()

查看更多
余生无你
7楼-- · 2019-01-02 18:08

As people have pointed out, this is due to Prototype.js - specifically versions prior to 1.7. I had a similar situation but had to have code that operated whether Prototype.js was there or not; this means I can't just delete the Array.prototype.toJSON as I'm not sure what relies on it. For that situation this is the best solution I came up with:

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

Hopefully it will help someone.

查看更多
登录 后发表回答