JSON.stringify () tableau bizarreness avec Prototype.js

j'essaye de comprendre ce qui a mal tourné avec ma sérialisation json, avoir la version actuelle de mon application avec et ancienne et je trouve des différences surprenantes dans la façon dont JSON.stringify() fonctionne (en utilisant la bibliothèque JSON de json.org).

Dans l'ancienne version de mon application:

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

me donne ceci;

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

dans la nouvelle version,

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

me donne ceci;

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

une idée de ce qui aurait pu changer pour faire la même bibliothèque mettre des citations autour des crochets de tableau dans la nouvelle version?

81
demandé sur Bergi 2009-04-02 20:48:12

11 réponses

depuis JSON.stringify a été l'expédition avec certains navigateurs récemment, je suggérerais de l'utiliser au lieu de Prototype de toJSON. Vous devriez alors vérifier pour la fenêtre.JSON & & window.JSON.stringify et inclure le json.org bibliothèque autrement (via document.createElement('script') ...). Pour résoudre les incompatibilités, utilisez:

if(window.Prototype) {
    delete Object.prototype.toJSON;
    delete Array.prototype.toJSON;
    delete Hash.prototype.toJSON;
    delete String.prototype.toJSON;
}
75
répondu Raphael Schweikert 2010-06-30 10:44:51

la fonction JSON.stringify() défini dans ECMAScript 5 et supérieur (Page 201-L'objet JSON, pseudo-code Page 205) , utilise la fonction toJSON () lorsqu'elle est disponible sur les objets.

Parce Que Prototype.js (ou d'une autre bibliothèque que vous utilisez) définit un Tableau.prototype.la méthode toJSON() de la fonction, les tableaux sont d'abord converties en chaînes de caractères à l'aide du Tableau.prototype.toJSON () puis chaîne de caractères Citée par JSON.stringify (), d'où les guillemets supplémentaires incorrects autour du tableau.

la solution est donc simple et triviale (c'est une version simplifiée de la réponse de Raphaël Schweikert):

delete Array.prototype.toJSON

cela produit bien sûr des effets secondaires sur les bibliothèques qui dépendent d'une propriété de fonction toJSON() pour les tableaux. Mais je trouve que c'est un inconvénient mineur compte tenu de l'incompatibilité avec ECMAScript 5.

il faut noter que L'objet JSON défini dans ECMAScript 5 est efficace mis en œuvre dans les navigateurs modernes et donc la meilleure solution est de se conformer à la norme et de modifier les bibliothèques existantes.

75
répondu Jean Vincent 2011-07-07 13:10:42

une solution possible qui n'affecterait pas les autres dépendances du Prototype serait:

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

ceci s'occupe de L'incompatibilité Tojson Array avec JSON.stringify et conserve aussi les fonctionnalités de toJSON car d'autres bibliothèques prototypes peuvent en dépendre.

14
répondu akkishore 2015-10-20 09:03:57

Modifier pour rendre un peu plus précis:

le bit de clé de problème de code est dans la bibliothèque JSON de JSON.org (et autres implémentations de l'objet JSON D'ECMAScript 5):

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

le problème est que la bibliothèque Prototype étend le tableau pour inclure une méthode toJSON, que l'objet JSON appellera dans le code ci-dessus. Quand l'objet JSON atteint la valeur du tableau il appelle toJSON sur le tableau qui est défini dans Prototype, et cette méthode renvoie une version string du tableau. Par conséquent, les citations autour des crochets de tableau.

si vous supprimez toJSON de L'objet Array, la bibliothèque JSON doit fonctionner correctement. Ou utilisez la bibliothèque JSON.

7
répondu Bob 2011-07-08 01:51:38

je pense qu'une meilleure solution serait d'inclure ce juste après que le prototype a été chargé

JSON = JSON || {};

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

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

cela rend la fonction prototype disponible en tant que JSON standard.stringify() et JSON.parse(), mais conserve le natif de JSON.parse () si elle est disponible, alors cela rend les choses plus compatibles avec les navigateurs plus anciens.

4
répondu Benjamin 2014-01-07 12:22:31

Je ne parle pas couramment le Prototype, mais j'ai vu ça dans son docs :

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

Je ne suis pas sûr que cela aurait le même problème que l'encodage actuel, cependant.

Il ya aussi un plus long tutorial sur l'utilisation de JSON avec Prototype.

2
répondu Powerlord 2009-04-02 17:58:00

C'est le code que j'ai utilisé pour le même numéro:

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

vous vérifiez si Prototype existe, puis vous vérifiez la version. Si l'ancienne version utilise Object.toJSON (si défini) dans tous les autres cas, repli vers JSON.stringify ()

2
répondu Memos 2015-10-15 13:51:22

voilà comment je gère ça.

var methodCallString =  Object.toJSON? Object.toJSON(options.jsonMethodCall) :  JSON.stringify(options.jsonMethodCall);
1
répondu morgancodes 2009-04-02 17:36:49

ma solution tolérante vérifie si Array.prototype.toJSON est nuisible pour JSON stringify et le garde quand c'est possible de laisser le code environnant fonctionner comme prévu:

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
    }
}
1
répondu highmaintenance 2012-02-02 10:37:30

comme les gens l'ont souligné, ceci est dû au Prototype.js-spécifiquement les versions antérieures à la version 1.7. J'ai eu une situation similaire mais j'ai dû avoir un code qui fonctionnait si Prototype.js était là ou pas; cela signifie que je ne peux pas simplement supprimer le tableau.prototype.toJSON car je ne suis pas sûr de ce qui en dépend. Pour cette situation, c'est la meilleure solution que j'ai trouvée:

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

j'espère que ça aidera quelqu'un.

1
répondu polm23 2012-11-08 07:50:22

si vous ne voulez pas tout tuer, et avoir un code qui serait correct sur la plupart des navigateurs, vous pouvez le faire de cette façon:

(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));

cela semble complexe, mais cela n'est complexe que pour traiter la plupart des cas d'utilisation. L'idée principale est primordial JSON.stringify supprimer toJSON de l'objet passé en argument, puis d'appeler le vieux JSON.stringify , et enfin le restaurer.

0
répondu Jerska 2015-04-21 17:36:55