сортировка свойств объекта и JSON.преобразовать в строки



мое приложение имеет большой массив объектов, которые я строю и сохраняю на диск. К сожалению, когда объекты в массиве обрабатываются, а иногда и заменяются, свойства объектов перечислены в разных порядках (их порядок создания?). Когда я делаю JSON.stringify () на массиве и сохраните его, diff показывает свойства, перечисленные в разных порядках, что раздражает при попытке объединить данные дальше с помощью инструментов diff и слияния.



В Идеале Я необходимо отсортировать свойства объектов в алфавитном порядке перед выполнением операции stringify или как часть операции stringify. Существует код для манипулирования объектами массива во многих местах, и изменить их, чтобы всегда создавать свойства в явном порядке, было бы сложно.



предложения были бы очень приветствуются!



краткий пример:



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


выходные данные этих двух вызовов stringify отличаются и отображаются в различном виде мои данные,но мое приложение не заботится о порядке свойств.. объекты построены во многих отношениях и местах..

561   17  

17 ответов:

более простой, современный и в настоящее время браузер поддерживается подход просто так:

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

однако этот метод удаляет все вложенные объекты, на которые нет ссылок и которые не применяются к объектам в массивах. Вы также захотите сгладить объект сортировки, если вы хотите что-то вроде этого вывода:

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

вы можете сделать это с помощью этого:

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

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

чтобы сделать это программно с чем-то вы можете настроить себя, вам нужно нажать кнопку имена свойств объекта в массив, затем отсортируйте массив в алфавитном порядке и повторите этот массив (который будет находиться в правильном порядке) и выберите каждое значение из объекта в этом порядке. "hasOwnProperty" также проверяется, поэтому у вас определенно есть только собственные свойства объекта. Вот пример:

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

опять же, это должно гарантировать, что вы перебираете в алфавитном порядке.

наконец, принимая его дальше для самого простого способа, эта библиотека будет рекурсивно позволяет сортировать любой JSON, который вы передаете в него: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));

выход

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

Я думаю, что если вы контролируете поколение JSON (и похоже, что это так), то для ваших целей это может быть хорошим решением:json-stable-stringify

с сайта проекта:

детерминированный JSON.stringify () с пользовательской сортировкой, чтобы получить детерминированные хэши из строковых результатов

Если созданный JSON является детерминированным, вы должны легко различать / объединять его.

вы можете передать сортированный массив имен свойств в качестве второго аргумента JSON.stringify():

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

обновление 2018-7-24:

эта версия сортирует вложенные объекты и поддерживает массива, а также:

function sortObjByKey(value) {
  return (typeof value === 'object') ?
    (Array.isArray(value) ?
      value.map(sortObjByKey) :
      Object.keys(value).sort().reduce(
        (o, key) => {
          const v = value[key];
          o[key] = sortObjByKey(v);
          return o;
        }, {})
    ) :
    value;
}


function orderedJsonStringify(obj) {
  return JSON.stringify(sortObjByKey(obj));
}

тест:

  describe('orderedJsonStringify', () => {
    it('make properties in order', () => {
      const obj = {
        name: 'foo',
        arr: [
          { x: 1, y: 2 },
          { y: 4, x: 3 },
        ],
        value: { y: 2, x: 1, },
      };
      expect(orderedJsonStringify(obj))
        .to.equal('{"arr":[{"x":1,"y":2},{"x":3,"y":4}],"name":"foo","value":{"x":1,"y":2}}');
    });

    it('support array', () => {
      const obj = [
        { x: 1, y: 2 },
        { y: 4, x: 3 },
      ];
      expect(orderedJsonStringify(obj))
        .to.equal('[{"x":1,"y":2},{"x":3,"y":4}]');
    });

  });

устаревший ответ:

краткая версия в ES2016. Кредит на @codename, от https://stackoverflow.com/a/29622653/94148

function orderedJsonStringify(o) {
  return JSON.stringify(Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}));
}

Это то же самое, что и ответ Сатпала Сингха

function stringifyJSON(obj){
    keys = [];
    if(obj){
        for(var key in obj){
            keys.push(key);
        }
    }
    keys.sort();
    var tObj = {};
    var key;
    for(var index in keys){
        key = keys[index];
        tObj[ key ] = obj[ key ];
    }
    return JSON.stringify(tObj);
}

obj1 = {}; obj1.os="linux"; obj1.name="X";
stringifyJSON(obj1); //returns "{"name":"X","os":"linux"}"

obj2 = {}; obj2.name="X"; obj2.os="linux";
stringifyJSON(obj2); //returns "{"name":"X","os":"linux"}"

Вы можете добавить toJSON функция для вашего объекта, который можно использовать для настройки вывода. Внутри функции, при добавлении текущие свойства для нового объекта в определенном порядке должны сохранить порядок, когда строковые.

смотрите здесь:

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify

нет встроенного метода управления заказом, потому что данные JSON предназначены для доступа по ключу.

вот jsfiddle с небольшим примером:

http://jsfiddle.net/Eq2Yw/

попробуйте закомментировать toJSON функция - порядок свойств меняется на противоположный. Обратите внимание, что это может быть специфичным для браузера, т. е. заказ официально не поддерживается в спецификации. Он работает в текущей версии Firefox, но если вы хотите 100% надежное решение, возможно, вам придется написать свой собственный stringifier функция.

Edit:

тоже вижу это так вопрос, касающийся преобразовать в строки по недетерминированный вывод, особенно детали Дафф по поводу отличий браузера:

как детерминистически проверить, что объект JSON не был изменен?

рекурсивный и упрощенный ответ:

function sortObject(obj) {
    if(typeof obj !== 'object')
        return obj
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]]);       
    return temp;
}

var str = JSON.stringify(sortObject(obj), undefined, 4);

Я взял ответ от @Jason Parham и сделал некоторые улучшения

function sortObject(obj, arraySorter) {
    if(typeof obj !== 'object')
        return obj
    if (Array.isArray(obj)) {
        if (arraySorter) {
            obj.sort(arraySorter);
        }
        for (var i = 0; i < obj.length; i++) {
            obj[i] = sortObject(obj[i], arraySorter);
        }
        return obj;
    }
    var temp = {};
    var keys = [];
    for(var key in obj)
        keys.push(key);
    keys.sort();
    for(var index in keys)
        temp[keys[index]] = sortObject(obj[keys[index]], arraySorter);       
    return temp;
}

это устраняет проблему преобразования массивов в объекты, а также позволяет определить, как сортировать массивы.

пример:

var data = { content: [{id: 3}, {id: 1}, {id: 2}] };
sortObject(data, (i1, i2) => i1.id - i2.id)

выход:

{content:[{id:1},{id:2},{id:3}]}

работает с lodash, вложенными объектами, любым значением атрибута объекта:

function sort(myObj) {
  var sortedObj = {};
  Object.keys(myObj).sort().forEach(key => {
    sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : myObj[key]
  })
  return sortedObj;
}
JSON.stringify(sort(yourObj), null, 2)

он зависит от поведения Chrome и узла, что первый ключ, назначенный объекту, выводится первым JSON.stringify.

попробуй:

function obj(){
  this.name = '';
  this.os = '';
}

a = new obj();
a.name = 'X',
a.os = 'linux';
JSON.stringify(a);
b = new obj();
b.os = 'linux';
b.name = 'X',
JSON.stringify(b);

Я сделал функцию для сортировки объекта, и с обратного вызова .. которые на самом деле создают новый объект

function sortObj( obj , callback ) {

    var r = [] ;

    for ( var i in obj ){
        if ( obj.hasOwnProperty( i ) ) {
             r.push( { key: i , value : obj[i] } );
        }
    }

    return r.sort( callback ).reduce( function( obj , n ){
        obj[ n.key ] = n.value ;
        return obj;
    },{});
}

и вызовите его с объектом .

var obj = {
    name : "anu",
    os : "windows",
    value : 'msio',
};

var result = sortObj( obj , function( a, b ){
    return a.key < b.key  ;    
});

JSON.stringify( result )

, который печатает {"value":"msio","os":"windows","name":"anu"} , и для сортировки с значение .

var result = sortObj( obj , function( a, b ){
    return a.value < b.value  ;    
});

JSON.stringify( result )

, который печатает {"os":"windows","value":"msio","name":"anu"}

вы можете сортировать объект по имени свойства в EcmaScript 2015

function sortObjectByPropertyName(obj) {
    return Object.keys(obj).sort().reduce((c, d) => (c[d] = obj[d], c), {});
}

https://gist.github.com/davidfurlong/463a83a33b70a3b6618e97ec9679e490

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

если объекты в списке не имеют одинаковых свойств, создайте объединенный главный объект перед stringify:

let arr=[ <object1>, <object2>, ... ]
let o = {}
for ( let i = 0; i < arr.length; i++ ) {
  Object.assign( o, arr[i] );
}
JSON.stringify( arr, Object.keys( o ).sort() );
function FlatternInSort( obj ) {
    if( typeof obj === 'object' )
    {
        if( obj.constructor === Object )
        {       //here use underscore.js
            let PaireStr = _( obj ).chain().pairs().sortBy( p => p[0] ).map( p => p.map( FlatternInSort ).join( ':' )).value().join( ',' );
            return '{' + PaireStr + '}';
        }
        return '[' + obj.map( FlatternInSort ).join( ',' ) + ']';
    }
    return JSON.stringify( obj );
}

// пример как ниже. в каждом слое, для таких объектов, как {}, сглажены в сортировке ключей. для массивов, чисел или строк, сглаженных как/с JSON.преобразовать в строки.

FlatternInSort( { с:9, В: { Г: 4, З: 2, Д: 9 }, Ф:4, Гостей:[{х:8, ч:3},{а:3,Б:7}] } )

" {"F": 4,"a": [{"h": 3,"j": 8}, {"a": 3,"b": 7}],"b": {"e": 9,"y": 4,"z": 2},"c": 9}"

расширение ответа AJP, чтобы обрабатывать массивы:

function sort(myObj) {
    var sortedObj = {};
    Object.keys(myObj).sort().forEach(key => {
        sortedObj[key] = _.isPlainObject(myObj[key]) ? sort(myObj[key]) : _.isArray(myObj[key])? myObj[key].map(sort) : myObj[key]
    })
    return sortedObj;
}

здесь Array.sort метод, который может быть полезным для вас. Например:

yourBigArray.sort(function(a,b){
    //custom sorting mechanism
});

Comments

    Ничего не найдено.