43 votes

Existe-t-il un équivalent déterministe de JSON.stringify?

Selon le MDN documents JSON.stringify, https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify :

Propriétés de non-tableau des objets ne sont pas garantis pour être stringified dans un ordre particulier. Ne comptez pas sur la commande de propriétés à l'intérieur le même objet dans le stringification.

J'avais espéré pour déterminer si un objet a changé par la mise en cache une stringified version de l'objet, puis en le comparant à un par la suite stringified version de l'objet. Qui semblait beaucoup plus simple que de façon récursive de l'itération à travers l'objet et de faire des comparaisons. Le problème est que parce que le JSON.stringify fonction n'est pas déterministe, j'ai pu techniquement obtenir une chaîne de caractère quand je stringify le même objet.

Quelles autres options s'offrent à moi? Ou dois-je écrire un méchant fonction de comparaison pour déterminer l'objet de l'égalité?

11voto

Daff Points 22358

Je suis à peu près sûr que cela tient à la façon dont différents moteurs JavaScript enregistrent les propriétés des objets en interne. Prenons ceci par exemple:

 var obj = {
"1" : "test",
"0" : "test 2"
};

for(var key in obj) {
    console.log(key);
}
 

Cela enregistrera 1, 0 dans Firefox par exemple, mais 0, 1 dans V8 (Chrome et NodeJS). Donc, si vous devez être déterministe, il vous faudra probablement parcourir chaque clé pour la stocker dans un tableau, trier le tableau, puis définir chaque propriété séparément en effectuant une boucle dans ce tableau.

5voto

Jimmy Theis Points 408

Voici une implémentation d'un JSON.stringify () déterministe que j'ai écrit (utilise Underscore.js ). Il convertit les objets (non-array) de manière récursive en paires clé-valeur triées (en tant que tableaux), puis les stringifie. Post original de coderwall ici .

Stringify:

 function stringify(obj) {
  function flatten(obj) {
    if (_.isObject(obj)) {
      return _.sortBy(_.map(
          _.pairs(obj),
          function(p) { return [p[0], flatten(p[1])]; }
        ),
        function(p) { return p[0]; }
      );
    }
    return obj;
  }
  return JSON.stringify(flatten(obj));
}
 

Parse:

 function parse(str) {
  function inflate(obj, pairs) {
     _.each(pairs, function(p) {
      obj[p[0]] = _.isArray(p[1]) ?
        inflate({}, p[1]) :
        p[1];
    });
    return obj;
  }
  return inflate({}, JSON.parse(str));
}
 

3voto

stchangg Points 112

Utiliser Underscore ou Lodash:

 var sortByKeys = function(obj) {
  if (!_.isObject(obj)) {
    return obj;
  }
  var sorted = {};
  _.each(_.keys(obj).sort(), function(key) {
    sorted[key] = sortByKeys(obj[key]);
  });
  return sorted;
};

var sortedStringify = function() {
    arguments[0] = sortByKeys(arguments[0]);
    return JSON.stringify.apply(this, arguments);
};
 

Fonctionne dans les derniers Chrome et Firefox.

JSFiddle ici: http://jsfiddle.net/stchangg/ruC22/2/

3voto

stamat Points 352

Ces jours, j'ai été jouer avec la façon déterministe à stringify un objet et j'ai écrit l'objet de livraison stringify en JSON, qui résout le ci-dessus dilemme: http://stamat.wordpress.com/javascript-object-ordered-property-stringify/

Aussi, j'ai été jouer avec la coutume de la table de hachage des implémentations qui est également liée à ce sujet: http://stamat.wordpress.com/javascript-quickly-find-very-large-objects-in-a-large-array/

//SORT WITH STRINGIFICATION

var orderedStringify = function(o, fn) {
    var props = [];
    var res = '{';
    for(var i in o) {
        props.push(i);
    }
    props = props.sort(fn);

    for(var i = 0; i < props.length; i++) {
        var val = o[props[i]];
        var type = types[whatis(val)];
        if(type === 3) {
            val = orderedStringify(val, fn);
        } else if(type === 2) {
            val = arrayStringify(val, fn);
        } else if(type === 1) {
            val = '"'+val+'"';
        }

        if(type !== 4)
            res += '"'+props[i]+'":'+ val+',';
    }

    return res.substring(res, res.lastIndexOf(','))+'}';
};

//orderedStringify for array containing objects
var arrayStringify = function(a, fn) {
    var res = '[';
    for(var i = 0; i < a.length; i++) {
        var val = a[i];
        var type = types[whatis(val)];
        if(type === 3) {
            val = orderedStringify(val, fn);
        } else if(type === 2) {
            val = arrayStringify(val);
        } else if(type === 1) {
            val = '"'+val+'"';
        }

        if(type !== 4)
            res += ''+ val+',';
    }

    return res.substring(res, res.lastIndexOf(','))+']';
}

2voto

nehz Points 610

Récemment, j'ai eu un cas d'utilisation similaire. Le code suivant n'a pas de dépendances et fonctionne pour tous les navigateurs:

 function stringify(obj) {
  var type = Object.prototype.toString.call(obj);

  // IE8 <= 8 does not have array map
  var map = Array.prototype.map || function map(callback) {
    var ret = [];
    for (var i = 0; i < this.length; i++) {
      ret.push(callback(this[i]));
    }
    return ret;
  };

  if (type === '[object Object]') {
    var pairs = [];
    for (var k in obj) {
      if (!obj.hasOwnProperty(k)) continue;
      pairs.push([k, stringify(obj[k])]);
    }
    pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1 });
    pairs = map.call(pairs, function(v) { return '"' + v[0] + '":' + v[1] });
    return '{' + pairs + '}';
  }

  if (type === '[object Array]') {
    return '[' + map.call(obj, function(v) { return stringify(v) }) + ']';
  }

  return JSON.stringify(obj);
};
 

stringify([{b: {z: 5, c: 2, a: {z: 1, b: 2}}, a: 1}, [1, 2, 3]])

'[{"a":1,"b":{"a":{"b":2,"z":1},"c":2,"z":5}},[1,2,3]]'

stringify([{a: 1, b:{z: 5, c: 2, a: {b: 2, z: 1}}}, [1, 2, 3]])

'[{"a":1,"b":{"a":{"b":2,"z":1},"c":2,"z":5}},[1,2,3]]'

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X