Skip to content

Commit

Permalink
Implemented diffJson.
Browse files Browse the repository at this point in the history
It takes two objects, serializes them as canonical JSON, then does a line-based diff that ignores differences in trailing commas.

See discussion here: mochajs/mocha#1182
  • Loading branch information
papandreou committed Apr 3, 2014
1 parent ae33afb commit 2b2557b
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 2 deletions.
60 changes: 58 additions & 2 deletions diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,11 @@ var JsDiff = (function() {
while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
newPos++;
oldPos++;

this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
var value = newString[newPos];
if (this.useLongestToken && oldString[oldPos].length > value.length) {
value = oldString[oldPos];
}
this.pushComponent(basePath.components, value, undefined, undefined);
}
basePath.newPos = newPos;
return oldPos;
Expand Down Expand Up @@ -183,6 +186,52 @@ var JsDiff = (function() {
return retLines;
};

JsonDiff = new Diff();
JsonDiff.useLongestToken = true;
JsonDiff.tokenize = LineDiff.tokenize;
JsonDiff.equals = function(left, right) {
return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
};

function canonicalize(obj, stack) {
stack = stack || [];

var i;

for (var i = 0 ; i < stack.length ; i += 1) {
if (stack[i] === obj) {
return obj;
}
}

var canonicalizedObj;

if ('[object Array]' == {}.toString.call(obj)) {
stack.push(obj);
canonicalizedObj = new Array(obj.length);
for (i = 0 ; i < obj.length ; i += 1) {
canonicalizedObj[i] = canonicalize(obj[i], stack);
}
stack.pop();
} else if (typeof obj === 'object' && obj !== null) {
stack.push(obj);
canonicalizedObj = {};
var sortedKeys = [];
for (var key in obj) {
sortedKeys.push(key);
}
sortedKeys.sort();
for (i = 0 ; i < sortedKeys.length ; i += 1) {
var key = sortedKeys[i];
canonicalizedObj[key] = canonicalize(obj[key], stack);
}
stack.pop();
} else {
canonicalizedObj = obj;
}
return canonicalizedObj;
}

return {
Diff: Diff,

Expand All @@ -191,6 +240,13 @@ var JsDiff = (function() {
diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); },
diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },

diffJson: function(oldObj, newObj) {
return JsonDiff.diff(
JSON.stringify(canonicalize(oldObj), undefined, " "),
JSON.stringify(canonicalize(newObj), undefined, " ")
);
},

diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },

createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
Expand Down
16 changes: 16 additions & 0 deletions test/diffTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ describe('#diffLines', function() {
});
});

describe('#diffJson', function () {
it('should ignore trailing comma on the previous line when the property has been removed', function() {
var diffResult = diff.diffJson(
{a: 123, b: 456, c: 789},
{a: 123, b: 456});
diff.convertChangesToXML(diffResult).should.equal('{\n &quot;a&quot;: 123,\n &quot;b&quot;: 456,\n<del> &quot;c&quot;: 789\n</del>}');
});

it('should ignore the missing trailing comma on the last line when a property has been added after it', function() {
var diffResult = diff.diffJson(
{a: 123, b: 456},
{a: 123, b: 456, c: 789});
diff.convertChangesToXML(diffResult).should.equal('{\n &quot;a&quot;: 123,\n &quot;b&quot;: 456,\n<ins> &quot;c&quot;: 789\n</ins>}');
});
});

describe('convertToDMP', function() {
it('should output diff-match-patch format', function() {
var diffResult = diff.diffWords('New Value ', 'New ValueMoreData ');
Expand Down

0 comments on commit 2b2557b

Please sign in to comment.