178 votes

trier les propriétés des objets et JSON.stringify

Mon application contient un grand nombre d'objets, que je mets en chaîne et que je sauvegarde sur le disque. Malheureusement, lorsque les objets du tableau sont manipulés, et parfois remplacés, les propriétés des objets sont listées dans des ordres différents (leur ordre de création ?). Lorsque je fais JSON.stringify() sur le tableau et que je l'enregistre, un diff montre que les propriétés sont listées dans des ordres différents, ce qui est gênant lorsque l'on essaie de fusionner les données avec des outils de diff et de fusion.

Idéalement, j'aimerais trier les propriétés des objets par ordre alphabétique avant d'effectuer le stringify, ou dans le cadre de l'opération de stringify. Il existe du code pour manipuler les objets du tableau à de nombreux endroits, et il serait difficile de le modifier pour toujours créer les propriétés dans un ordre explicite.

Les suggestions sont les bienvenues !

Un exemple condensé :

obj = {}; obj.name="X"; obj.os="linux";
JSON.stringify(obj);
obj = {}; obj.os="linux"; obj.name="X";
JSON.stringify(obj);

Les résultats de ces deux appels stringify sont différents, et apparaissent dans un diff de mes données, mais mon application ne se soucie pas de l'ordre des propriétés. Les objets sont construits de plusieurs façons et à plusieurs endroits.

143voto

marksyzm Points 1092

L'approche plus simple, moderne et actuellement prise en charge par les navigateurs est la suivante :

JSON.stringify(sortMyObj, Object.keys(sortMyObj).sort());

Toutefois, cette méthode supprime tous les objets imbriqués qui ne sont pas référencés et ne s'applique pas aux objets contenus dans des tableaux. Vous devrez également aplatir l'objet de tri si vous souhaitez obtenir ce type de résultat :

{"a":{"h":4,"z":3},"b":2,"c":1}

Vous pouvez le faire avec ça :

var flattenObject = function(ob) {
    var toReturn = {};

    for (var i in ob) {
        if (!ob.hasOwnProperty(i)) continue;

        if ((typeof ob[i]) == 'object') {
            var flatObject = flattenObject(ob[i]);
            for (var x in flatObject) {
                if (!flatObject.hasOwnProperty(x)) continue;

                toReturn[i + '.' + x] = flatObject[x];
            }
        } else {
            toReturn[i] = ob[i];
        }
    }
    return toReturn;
};
var myFlattenedObj = flattenObject(sortMyObj);
JSON.stringify(myFlattenedObj, Object.keys(myFlattenedObj).sort());

Pour le faire de manière programmatique avec quelque chose que vous pouvez modifier vous-même, vous devez pousser les noms des propriétés de l'objet dans un tableau, puis trier le tableau par ordre alphabétique et itérer dans ce tableau (qui sera dans le bon ordre) et sélectionner chaque valeur de l'objet dans cet ordre. La case "hasOwnProperty" est également cochée pour que vous n'ayez que les propriétés de l'objet. Voici un exemple :

var obj = {"a":1,"b":2,"c":3};

function iterateObjectAlphabetically(obj, callback) {
    var arr = [],
        i;

    for (i in obj) {
        if (obj.hasOwnProperty(i)) {
            arr.push(i);
        }
    }

    arr.sort();

    for (i = 0; i < arr.length; i++) {
        var key = obj[arr[i]];
        //console.log( obj[arr[i]] ); //here is the sorted value
        //do what you want with the object property
        if (callback) {
            // callback returns arguments for value, key and original object
            callback(obj[arr[i]], arr[i], obj);
        }
    }
}

iterateObjectAlphabetically(obj, function(val, key, obj) {
    //do something here
});

Encore une fois, cela devrait garantir que l'itération se fera par ordre alphabétique.

Enfin, pour aller plus loin dans la simplicité, cette bibliothèque vous permettra récursivement de trier tout JSON que vous lui passerez : https://www.npmjs.com/package/json-stable-stringify

var stringify = require('json-stable-stringify');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));

Sortie

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}

71voto

Jor Points 570

Je ne comprends pas pourquoi la complexité des meilleures réponses actuelles est nécessaire, pour obtenir toutes les clés de manière récursive. A moins qu'une performance parfaite ne soit nécessaire, il me semble que nous pouvons simplement appeler JSON.stringify() deux fois, la première fois pour obtenir toutes les clés, et la deuxième fois, pour vraiment faire le travail. De cette façon, toute la complexité de la récursion est gérée par stringify Nous savons qu'il connaît son métier et qu'il sait comment traiter chaque type d'objet :

function JSONstringifyOrder(obj, space)
{
    const allKeys = new Set();
    JSON.stringify(obj, (key, value) => (allKeys.add(key), value));
    return JSON.stringify(obj, Array.from(allKeys).sort(), space);
}

Ou si vous souhaitez prendre en charge des navigateurs plus anciens :

function JSONstringifyOrder(obj, space)
{
    var allKeys = [];
    var seen = {};
    JSON.stringify(obj, function (key, value) {
        if (!(key in seen)) {
            allKeys.push(key);
            seen[key] = null;
        }
        return value;
    });
    allKeys.sort();
    return JSON.stringify(obj, allKeys, space);
}

48voto

Stijn de Witt Points 3515

Je pense que si vous contrôlez la génération du JSON (et il semble que ce soit le cas), cette solution pourrait être la bonne pour vos besoins : json-stable-stringify

Depuis le site web du projet :

déterministe JSON.stringify() avec un tri personnalisé pour obtenir des hachages déterministes à partir de résultats stringifiés

Si le JSON produit est déterministe, vous devriez être en mesure de le différencier/fusionner facilement.

42voto

David Furlong Points 292

JSON.stringify() fonction de remplacement pour que les clés des objets soient triées dans la sortie (supporte les objets profondément imbriqués).

const replacer = (key, value) =>
value instanceof Object && !(value instanceof Array) ? 
    Object.keys(value)
    .sort()
    .reduce((sorted, key) => {
        sorted[key] = value[key];
        return sorted 
    }, {}) :
    value;

// Usage
// JSON.stringify({c: 1, a: { d: 0, c: 1, e: {a: 0, 1: 4}}}, replacer);

Page Gist de GitHub aquí .

33voto

Vous pouvez passer un tableau trié des noms de propriétés comme deuxième argument de la fonction JSON.stringify() :

JSON.stringify(obj, Object.keys(obj).sort())

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