Object keys in a Javascript maintain insertion order.
For some crypto tasks, such as hashing and signing, it's desirable to have a normalized view of the data.
Fortunately it's easy enough to recursively sort object keys using just a few lines of code:
function replacer (key, value) {
switch (Object.prototype.toString.call(value)) {
// sort object keys lexicographically
case '[object Object]':
return Object.fromEntries(Object.entries(value).sort())
default:
return value
}
}
Example
You can use it as you normally would with JSON.stringify():
const example = {
"isbn": "123-456-222",
"author": {
"lastname": "Doe",
"firstname": "Jane"
},
"editor": {
"lastname": "Smith",
"firstname": "Jane"
},
"title": "The Ultimate Database Study Guide",
"category": [
"Non-Fiction",
"Technology"
]
}
> JSON.stringify(example, replacer, 2)
{
"author": {
"firstname": "Jane",
"lastname": "Doe"
},
"category": [
"Non-Fiction",
"Technology"
],
"editor": {
"firstname": "Jane",
"lastname": "Smith"
},
"isbn": "123-456-222",
"title": "The Ultimate Database Study Guide"
}
Additional types
It's also possible to extend this method to other types:
function replacer (key, value) {
switch (Object.prototype.toString.call(value)) {
// sort object keys lexicographically
case '[object Object]':
return Object.fromEntries(Object.entries(value).sort())
// Unicode Normalization Form KC
case '[object String]':
return value.normalize('NFKC')
// round floats to 7 decimal places of precision
case '[object Number]':
return value % 1 === 0
? value
: Math.round(parseFloat(value) * 1e7) / 1e7
default:
return value
}
}
