JSON diffing
I have now created a method for returning structural differences between two JSON objects. This completes my work with making a JSON merge and JSON diff implementation (although it will most certainly undergo future revisions and quirk resolving changes).
Why? Say for instance you have two JSON structures, json1 and json2. The object json1 holds an original set of data, whereas json2 represents an updated edition of json1. With the diff method you can programatically extract the differences between the two. This could e.g. be used to further limit the amount of data transmitted between the client/server (e.g. with ajax requests), without the need for specialized client side code tracking data changes. When you need to transmit the changes you simply diff the latest set received with the current.
Read my earlier article regarding JSON merging here.
Both the merge and diff methods I have created, can operate on cyclic (self-referencing) JSON structures.
Updated: Now correctly handles arrays. Please note that the ordering of items in the arrays are considered application critical. Thus, the array [1,2] is not considered similar to [2,1].
The diff and merge implementations require the Array.indexOf, Object.isArray and Object.keys methods from the prototype framework.
Further information and demos are available here:
jsonDiff and jsonMerge.
Here is the new implementation with the diff and merge methods (validates in JSLint):
-
// Author: Michael Schøler, 2008
-
// LGPL license, use as you like, don’t hold me responsible for success or failure though
-
Array.prototype.compareTo = function(compareAry) {
-
if (this.length === compareAry.length) {
-
var i;
-
for (i = 0; i < compareAry.length; i+=1) {
-
if (Object.isArray(this[i]) === true) {
-
if (this[i].compareTo(compareAry[i]) === false) {
-
return false;
-
}
-
continue;
-
}
-
else if (this[i] !== compareAry[i]) {
-
return false;
-
}
-
}
-
return true;
-
}
-
return false;
-
};
-
var jsonDataHandler = {
-
merge: function(j1, j2) {
-
if (typeof this.merging === “undefined” || this.merging === 0) {
-
this.mergeCyclicCheck = [];
-
this.merging = 0;
-
}
-
this.merging += 1;
-
if (typeof j1 === “undefined”) {
-
j1 = {};
-
}
-
if (typeof j2 === “undefined”) {
-
j2 = {};
-
}
-
if (typeof this.mergeCyclicCheck === “undefined”) {
-
this.mergeCyclicCheck = [];
-
}
-
var key;
-
for (key in j2) if (j2.hasOwnProperty(key)) {
-
if (typeof j1[key] === “undefined”) {
-
j1[key] = j2[key];
-
}
-
else {
-
if (typeof j2[key] === “object”) {
-
if (this.mergeCyclicCheck.indexOf(j1[key]) >= 0) {
-
break;
-
}
-
this.merge(j1[key], j2[key]);
-
this.mergeCyclicCheck.push(j1[key]);
-
}
-
else {
-
j1[key] = j2[key];
-
}
-
}
-
}
-
this.merging -= 1;
-
},
-
diff: function(j1, j2) {
-
if (typeof this.diffing === “undefined” || this.diffing === 0) {
-
this.diffCyclicCheck = [];
-
this.diffing = 0;
-
}
-
var diffRes = {};
-
this.diffing += 1;
-
if (typeof j1 === “undefined”) {
-
j1 = {};
-
}
-
if (typeof j2 === “undefined”) {
-
j2 = {};
-
}
-
if (typeof this.diffCyclicCheck === “undefined”) {
-
this.diffCyclicCheck = [];
-
}
-
var key, bDiff;
-
for (key in j2) if (j2.hasOwnProperty(key)) {
-
bDiff = false;
-
if (typeof j1[key] === “undefined” || typeof j1[key] != typeof j2[key]) {
-
bDiff = true;
-
}
-
else if (j1[key] !== j2[key]) {
-
if (typeof j2[key] === “object”) {
-
if (this.diffCyclicCheck.indexOf(j2[key]) >= 0) {
-
break;
-
}
-
else if (Object.isArray(j2[key])) {
-
if (j1[key].length !== j2[key].length || j1[key] !== j2[key]) {
-
if (j2[key].compareTo(j1[key]) === false) {
-
bDiff = true;
-
}
-
}
-
}
-
else if (typeof j1[key] === “object”) {
-
var dR = this.diff(j1[key], j2[key]);
-
if (Object.keys(dR).length > 0) {
-
diffRes[key] = dR;
-
}
-
}
-
else {
-
bDiff = true;
-
}
-
this.diffCyclicCheck.push(j2[key]);
-
}
-
else if (j1[key] !== j2[key]) {
-
bDiff = true;
-
}
-
}
-
if (bDiff) {
-
diffRes[key] = j2[key];
-
}
-
}
-
for (key in j1) if (j1.hasOwnProperty(key)) {
-
bDiff = false;
-
if (typeof j2[key] === “undefined”) {
-
diffRes[key] = j1[key];
-
}
-
}
-
this.diffing -= 1;
-
return diffRes;
-
}
-
};
You are free to download jsonDataHandler.zip which is a zip archive of the code in plain javascript and in JSMin minified format. The code is LGPL licensed (read more).
Enjoy!



Nice! That just might come in handy!
D. 16/01-2008 Kl. 07:07:33
Hi Michael,
Thanks for your work on this. I’m working on a js program that needs json diffing, and I’m trying to work some examples. I tried the following examples to diff, and it seems like the elements that are the same are still getting returned. Do you have any insight on why this might be? All I changed was one value from lcsh to lcsh2, so that is all i would expect to see back from the diff. Thanks.
json1: [[{"bb68609635":"lcsh"},{"bb17073259":"Anti-fascist movements"},{"bb04445165":"Spain"},{"bb17073259":"Posters"}],[{"bb68609635":"lcsh"},{"bb04445165":"Spain"},{"bb17073259":"History"},{"bb9147665h":"Civil War, 1936-1939"},{"bb17073259":"Propaganda"}]]
json2:
[[{"bb68609635":"lcsh"},{"bb17073259":"Anti-fascist movements"},{"bb04445165":"Spain"},{"bb17073259":"Posters"}],[{"bb68609635":"lcsh2"},{"bb04445165":"Spain"},{"bb17073259":"History"},{"bb9147665h":"Civil War, 1936-1939"},{"bb17073259":"Propaganda"}]]
results i’m getting:
{”0″: [{"bb68609635": "lcsh"}, {"bb17073259": "Anti-fascist movements"}, {"bb04445165": "Spain"}, {"bb17073259": "Posters"}], “1″: [{"bb68609635": "lcsh2"}, {"bb04445165": "Spain"}, {"bb17073259": "History"}, {"bb9147665h": "Civil War, 1936-1939"}, {"bb17073259": "Propaganda"}]}
thanks!
D. 25/02-2008 Kl. 23:08:59
please disregard, this was an issue in my code, not yours. thanks again for this!
D. 26/02-2008 Kl. 01:02:41
This looks very interesting. Have you tried integrating something like this with a scm like git for projects that include JSON files that need structural diffing rather than a purely line based comparison? I’m trying to find information about how to to do structural JSON diffing for max/msp projects managed by git.
D. 26/02-2009 Kl. 11:19:44
Thanks! No, I have not tried using the diffs in that manner. I’d like to know about your results, and if you found my jsonDiff useful for the task.
D. 26/02-2009 Kl. 18:06:51