排序对象属性和 JSON.stringify

2022-08-30 04:07:19

我的应用程序具有大量对象,我将其字符串化并保存到磁盘。不幸的是,当数组中的对象作,有时被替换时,对象上的属性以不同的顺序列出(它们的创建顺序?)。当我在数组上执行JSON.stringify()并保存它时,diff会显示以不同顺序列出的属性,这在尝试使用diff和合并工具进一步合并数据时很烦人。

理想情况下,我想在执行字符串化之前按字母顺序对对象的属性进行排序,或者作为字符串化操作的一部分。有许多地方都有用于操作数组对象的代码,并且更改这些代码以始终以显式顺序创建属性将很困难。

建议将是最欢迎的!

一个简明扼要的例子:

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

这两个字符串化调用的输出是不同的,并且显示在我的数据差异中,但我的应用程序不关心属性的顺序。对象以多种方式和地点构造。


答案 1

更简单,现代且当前浏览器支持的方法很简单:

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;
};
var myFlattenedObj = flattenObject(sortMyObj);
JSON.stringify(myFlattenedObj, Object.keys(myFlattenedObj).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}

答案 2

我不明白为什么需要当前最佳答案的复杂性,以递归方式获取所有密钥。除非需要完美的性能,否则在我看来,我们只需要调用两次,第一次是获取所有密钥,第二次是真正完成工作。这样,所有的递归复杂性都由 处理,我们知道它知道它的东西,以及如何处理每个对象类型:JSON.stringify()stringify

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

或者,如果您想支持较旧的浏览器:

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