最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Javascript recursively order object and nested objects as well as arrays - Stack Overflow

programmeradmin1浏览0评论

I am trying to get the same results as pythons json.dumps() with sort by keys enabled. This is preformed as a pre-request script for Postman to generate the request hash. The output needs to be sorted valid json which is used as input for hashing. I am new to javascript and see many old answers claiming that objects in javascript cannot be sorted. However there must be a solution to generate the hash given the criteria.

  • The object structure cannot be changed.
  • It only needs to support Chrome.
  • I can use libraries.
  • requestParams can contain nested objects and arrays which need to be sorted at any depth.

This is my current code. In the Chrome console the object preview for sortedResult is unsorted, however when I expand the object and sub-objects the Chrome console shows sortedResult as sorted, exactly the way it should be. This gives me the impression the sortObject is working. However requestOrdered returns the valid json object but it is not sorted. My initial thoughts are that maybe JSON.stringify() is unsorting it.

const requestRebuilt = {"username": user, "password": password, "sTime": time, "function": function,
                 "functionParams": requestParams, "salt": salt};

function sortObject(object){  
    var keys = _.keys(object);
    var sortedKeys = _.sortBy(keys, function(key){  
        //console.log(key);
        return key; 
    });
    var sortedObj = {};
    var sortedObjJson = "";

    for(var index in keys){
        var key = keys[index];
        //console.log(key + ' ' + typeof object[key]);

        if(typeof object[key] == 'object' && !(object[key] instanceof Array)){
            sortedObj[key] = sortObject(object[key]);
        } else if(object[key] instanceof Array) {
            //sortedObj[key] = object[key].sort();
            var arrayLength = object[key].length;
            for (var i = 0; i < arrayLength; i++) {
                sortedObj[key] = sortObject(object[key][i]);
                //console.log(object[key][i]);
            }
        } else {
            sortedObj[key] = object[key];
        }
    }
    return sortedObj;
}
const sortedResult = sortObject(requestRebuilt);
console.log(sortedResult);

const requestOrdered = JSON.stringify(sortedResult);
console.log(requestOrdered);

var hash = CryptoJS.SHA256(requestOrdered).toString();
postman.setGlobalVariable("hash", hash);

Example input:

{
    "username": "[email protected]",
    "sTime": "2016-03-04T13:53:37Z",
    "function": "begin",
    "functionParams": {
        "tip": "ABC123FFG",   
        "pad": 4 ,
        "passenger": [{
            "firstName": "John",
            "phone": 1234567890,
            "email": "[email protected]",
            "dateOfBirth": "1915-10-02T00:00:00Z",
            "bans": {
                "weight": 9,
                "count": 2
            }
        }
    ]},
    "salt": "00d878f5e203",
    "pep": "sdeODQ0T"
}

In python this is done by the following:

ordered = json.dumps(
   {"username": user, "password": password, "time": time, "function": function, "functionParams": functionParams, "salt": salt}
    sort_keys=True, separators=(',', ':'))

Result of ordered:

{"function":"begin","functionParams":{"passenger":[{"bans":{"count":2,"weight":9},"dateOfBirth":"1915-10-02T00:00:00Z","email":"[email protected]","firstName":"John","phone":1234567890}],"pad":4,"tip":"ABC123FFG"},"pep":"sdeODQ0T","salt":"00d878f5e203","sTime":"2016-03-04T13:53:37Z","username":"[email protected]"}

Pretty printed for easier reading but actual result should not have spaces or new lines:

    {
      "function": "begin",
      "functionParams": {
        "passenger": [
          {
            "bans": {
              "count": 2,
              "weight": 9
            },
            "dateOfBirth": "1915-10-02T00:00:00Z",
            "email": "[email protected]",
            "firstName": "John",
            "phone": 1234567890
          }
        ],
        "pad": 4,
        "tip": "ABC123FFG"
      },
      "pep": "sdeODQ0T",
      "salt": "00d878f5e203",
      "sTime": "2016-03-04T13:53:37Z",
      "username": "[email protected]"
    }

I am trying to get the same results as pythons json.dumps() with sort by keys enabled. This is preformed as a pre-request script for Postman to generate the request hash. The output needs to be sorted valid json which is used as input for hashing. I am new to javascript and see many old answers claiming that objects in javascript cannot be sorted. However there must be a solution to generate the hash given the criteria.

  • The object structure cannot be changed.
  • It only needs to support Chrome.
  • I can use libraries.
  • requestParams can contain nested objects and arrays which need to be sorted at any depth.

This is my current code. In the Chrome console the object preview for sortedResult is unsorted, however when I expand the object and sub-objects the Chrome console shows sortedResult as sorted, exactly the way it should be. This gives me the impression the sortObject is working. However requestOrdered returns the valid json object but it is not sorted. My initial thoughts are that maybe JSON.stringify() is unsorting it.

const requestRebuilt = {"username": user, "password": password, "sTime": time, "function": function,
                 "functionParams": requestParams, "salt": salt};

function sortObject(object){  
    var keys = _.keys(object);
    var sortedKeys = _.sortBy(keys, function(key){  
        //console.log(key);
        return key; 
    });
    var sortedObj = {};
    var sortedObjJson = "";

    for(var index in keys){
        var key = keys[index];
        //console.log(key + ' ' + typeof object[key]);

        if(typeof object[key] == 'object' && !(object[key] instanceof Array)){
            sortedObj[key] = sortObject(object[key]);
        } else if(object[key] instanceof Array) {
            //sortedObj[key] = object[key].sort();
            var arrayLength = object[key].length;
            for (var i = 0; i < arrayLength; i++) {
                sortedObj[key] = sortObject(object[key][i]);
                //console.log(object[key][i]);
            }
        } else {
            sortedObj[key] = object[key];
        }
    }
    return sortedObj;
}
const sortedResult = sortObject(requestRebuilt);
console.log(sortedResult);

const requestOrdered = JSON.stringify(sortedResult);
console.log(requestOrdered);

var hash = CryptoJS.SHA256(requestOrdered).toString();
postman.setGlobalVariable("hash", hash);

Example input:

{
    "username": "[email protected]",
    "sTime": "2016-03-04T13:53:37Z",
    "function": "begin",
    "functionParams": {
        "tip": "ABC123FFG",   
        "pad": 4 ,
        "passenger": [{
            "firstName": "John",
            "phone": 1234567890,
            "email": "[email protected]",
            "dateOfBirth": "1915-10-02T00:00:00Z",
            "bans": {
                "weight": 9,
                "count": 2
            }
        }
    ]},
    "salt": "00d878f5e203",
    "pep": "sdeODQ0T"
}

In python this is done by the following:

ordered = json.dumps(
   {"username": user, "password": password, "time": time, "function": function, "functionParams": functionParams, "salt": salt}
    sort_keys=True, separators=(',', ':'))

Result of ordered:

{"function":"begin","functionParams":{"passenger":[{"bans":{"count":2,"weight":9},"dateOfBirth":"1915-10-02T00:00:00Z","email":"[email protected]","firstName":"John","phone":1234567890}],"pad":4,"tip":"ABC123FFG"},"pep":"sdeODQ0T","salt":"00d878f5e203","sTime":"2016-03-04T13:53:37Z","username":"[email protected]"}

Pretty printed for easier reading but actual result should not have spaces or new lines:

    {
      "function": "begin",
      "functionParams": {
        "passenger": [
          {
            "bans": {
              "count": 2,
              "weight": 9
            },
            "dateOfBirth": "1915-10-02T00:00:00Z",
            "email": "[email protected]",
            "firstName": "John",
            "phone": 1234567890
          }
        ],
        "pad": 4,
        "tip": "ABC123FFG"
      },
      "pep": "sdeODQ0T",
      "salt": "00d878f5e203",
      "sTime": "2016-03-04T13:53:37Z",
      "username": "[email protected]"
    }
Share asked Mar 5, 2016 at 8:16 OriginalsOriginals 451 silver badge5 bronze badges 3
  • Object keys are not ordered in JavaScript. Thus you cannot use JSON.stringify to achieve this. – Antti Haapala -- Слава Україні Commented Mar 5, 2016 at 8:19
  • Is there some sort of work around? If javascript cannot preserve order how can you hash an object properly? – Originals Commented Mar 5, 2016 at 8:21
  • As you have noted, it is NOT possible to order object keys in JavaScript. What you can instead do is create an array of the sort [ ['key1', 'value1'], ['key2', 'value2'] ] to preserve the order you are interested in – Jibi Abraham Commented Mar 5, 2016 at 9:01
Add a ment  | 

1 Answer 1

Reset to default 12

It's a mon misconception that "object keys are not ordered" in javascript. MDN states that

Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property ing first (at least for properties not on the prototype).

and ES2015 makes this behaviour standard:

For each own property key P of O that is a String but is not an integer index, in property creation order...

That is, you can rely on the fact that object properties are always iterated in the insertion order (unless you're using delete, see here for details).

So, to sort keys in some object just create a new object and add keys to it in the sorted order:

function sortKeys(x) {
    if (typeof x !== 'object' || !x)
        return x;
    if (Array.isArray(x))
        return x.map(sortKeys);
    return Object.keys(x).sort().reduce((o, k) => ({...o, [k]: sortKeys(x[k])}), {});
}

////

obj = {
    "username": "[email protected]",
    "sTime": "2016-03-04T13:53:37Z",
    "function": "begin",
    "functionParams": {
        "tip": "ABC123FFG",
        "pad": 4,
        "passenger": [{
            "firstName": "John",
            "phone": 1234567890,
            "email": "[email protected]",
            "dateOfBirth": "1915-10-02T00:00:00Z",
            "bans": {
                "weight": 9,
                "count": 2
            }
        }
        ]
    },
    "salt": "00d878f5e203",
    "pep": "sdeODQ0T"
}


sorted = sortKeys(obj);
console.log(sorted);

发布评论

评论列表(0)

  1. 暂无评论