How to deterministically verify that a JSON object hasn't been modified?
Solution 1:
I am pretty sure this is because of the way different JavaScript engines keep track of object properties internally. Take this for example:
var obj = {
"1" : "test",
"0" : "test 2"
};
for(var key in obj) {
console.log(key);
}
This will log 1, 0 in e.g. Firefox, but 0, 1 in V8 (Chrome and NodeJS). So if you need to be deterministic, you will probably have to iterate through each key store it in an array, sort the array and then stringify each property separately by looping through that array.
Solution 2:
You may want to try JSON.sortify, a little helper that I wrote.
In contrast to the answers given so far, it
- works with any level of nesting
- can handle numeric keys
- escapes special characters in keys
- accepts the
space
parameter as well as the little usedreplacer
parameter - throws a TypeError on cyclical references (as it should)
- filters
undefined
values and functions - respects
toJSON()
Solution 3:
Here's an implementation of a deterministic JSON.stringify() that I wrote (uses Underscore.js). It converts (non-array) objects recursively into sorted key-value pairs (as Arrays), then stringifies those. Original coderwall post here.
Stringify:
function stringify(obj) {
function flatten(obj) {
if (_.isObject(obj)) {
return _.sortBy(_.map(
_.pairs(obj),
function(p) { return [p[0], flatten(p[1])]; }
),
function(p) { return p[0]; }
);
}
return obj;
}
return JSON.stringify(flatten(obj));
}
Parse:
function parse(str) {
function inflate(obj, pairs) {
_.each(pairs, function(p) {
obj[p[0]] = _.isArray(p[1]) ?
inflate({}, p[1]) :
p[1];
});
return obj;
}
return inflate({}, JSON.parse(str));
}