Skip to content

PaulHuizer/JsonDiffPatch

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JsonDiffPatch

Diff & Patch for JavaScript objects and arrays (ie. any JSON serializable structure)

JsonDiffPatch is a small library that allows to diff object graphs, create a patch (in pure JSON), and apply it to update an original version.

Now available on npm:

npm install jsondiffpatch

or

bower install jsondiffpatch


  • Could be used for logging, audit, remote (client-server) synchronization of changes, etc.
  • Minified version is < 6KB
  • Works in browsers and server (Node.j or any CommonJS env), open test page to check other browsers.
  • Automatically detect environment support and load as CommonJS module (eg: node.js), anonymous AMD module (eg: using RequireJS on the browser, no globals), or as browser global.
  • For long text diffs uses google-diff_match_patch library if loaded (other text diff libs can be plugged in)
  • Arrays diffs are smart!
    • Using LCS (the same type of algorithm used by popular text diff tools on lines of text) insertions and deletions are detected efficiently.
    • Also detects items moved on the same array (a refinement to LCS). Patching will only move the item in the array, and inner changes in the moved object are diffed/patched too.
    • Works with objects in the array if you provide a hash function, eg: jsondiffpatch.config.objectHash = function(obj) { return obj.id || JSON.stringify(obj); };. NOTE: If no objectHash function is configured Array items are compared using just ===, meaning { a: 1 } is different from { a: 1 } unless they reference the same instance, this is by design, because object equality is a problem without trivial solution. Some use-cases are fine with just === (the default), others need comparison by an id field, others full JSON stringify, be sure to provide yours if default is not enough, as in DEMO page)
  • Reverse a diff and unpatch (eg. revert object to its original state based on diff)
  • Optional lib included for visualizing diffs as html

Delta Legend

  • Objects on the graph means that it's a node in the diff tree and will continue recursively

    • _t: (special member) indicates the type of node, a means array, otherwise it's an object.
    • in arrays, N indicates index on the new array, _N means index at the original array.
  • Arrays in the delta means that the node has changed

    • [newValue] -> added
    • [oldValue, newValue] -> modified
    • [oldValue, 0, 0] -> deleted
    • [textDiff, 0, 2] -> text diff
    • ["", N, 3] -> element was moved to N

###Example

Object diffing:

    // sample data
    var country = {
        name: "Argentina",
        capital: "Buenos Aires",
        independence: new Date(1816, 6, 9),
        unasur: true
    };

    // clone country, using dateReviver for Date objects
    var country2 = JSON.parse(JSON.stringify(country),jsondiffpatch.dateReviver);

    // make some changes
    country2.name = "Rep�blica Argentina";
    country2.population = "41324992";
    delete country2.capital;

    var delta = jsondiffpatch.diff(country,country2);

    /*
    delta = {
        "name":["Argentina","Rep�blica Argentina"], // old value, new value
        "population":["41324992"], // new value
        "capital":["Buenos Aires",0,0] // deleted
    }
    */

    // patch original
    jsondiffpatch.patch(country, delta);

    // reverse diff
    var reverseDelta = jsondiffpatch.reverse(delta);
    // also country2 can be return to original value with: jsondiffpatch.unpatch(country2, delta);

    var delta2 = jsondiffpatch.diff(country,country2);

    // delta2 is undefined, no difference

Array diffing:

    // sample data
    var country = {
        name: "Argentina",
        cities: [
        {
            name: 'Buenos Aires',
            population: 13028000,
        },
        {
            name: 'C�rdoba',
            population: 1430023,
        },
        {
            name: 'Rosario',
            population: 1136286,
        },
        {
            name: 'Mendoza',
            population: 901126,
        },
        {
            name: 'San Miguel de Tucum�n',
            population: 800000,
        }
        ]
    };

    // clone country
    var country2 = JSON.parse(JSON.stringify(country));

    // delete C�rdoba
    country.cities.splice(1, 1);

    // add La Plata
    country.cities.splice(4, 0, {
        name: 'La Plata'
        });

    // modify Rosario, and move it
    var rosario = country.cities.splice(1, 1)[0];
    rosario.population += 1234;
    country.cities.push(rosario);

    // match objects by name
    jsondiffpatch.config.objectHash = function(obj) {
        return obj.name;
    }

    var delta = jsondiffpatch.diff(country,country2);

    /*
    delta = {
        "cities": {
            "1": [
                // inserted at index 1
                {
                    "name": "C�rdoba",
                    "population": 1430023
                }]
            ,
            "2": {
                // population modified at index 2 (Rosario)
                "population": [
                    1137520,
                    1136286
                ]
            },
            "_t": "a",
            "_3": [
                // removed from index 3
                {
                    "name": "La Plata"
                },0,0],
            "_4": [
                // move from index 4 to index 2
                '',2,3]
        }
    }
    */

For more complex cases (nested objects or arrays, long text diffs) check unit tests in /test/test.js

To use as AMD module (eg: using RequireJS on the browser):

require('jsondiffpatch', function(jsondiffpatch){

    // code using jsondiffpatch

});

// a module that depends on jsondiffpatch
define('mytexteditor.visualcomparer', ['jsondiffpatch'], function(jsondiffpatch){

    // module implementation using jsondiffpatch

});

Targeted platforms

  • Tested on Chrome, FireFox, IE7+, to check other browsers open test page to run unit tests.
  • Node.js

QUnit is used for unit testing. Just open the test page on your preferred browser.

To run tests on Node.js on jsondiffpatch root folder:

    npm i
    npm test

Minification

A minified version is provided as jsondiffpatch.min.js To regenerate that file run (npm i is required as uglifyjs is used):

    npm i
    npm run-script minify

Including JsonDiffPatch in your application

Install using npm:

npm install jsondiffpatch

or, Download the latest release from the web site (http://github.com/benjamine/JsonDiffPatch) and copy jsondiffpatch.min.js to a suitable location. To support text diffs include Google's diff_match_patch.

Then include it in your HTML like so:

<script type="text/javascript" src="/path/to/jsondiffpatch.min.js"></script>
<script type="text/javascript" src="/path/to/diff_match_patch_uncompressed.js"></script>

Note: you can use JsonDiffPatch on browserless JavaScript environments too (as Node.js, or Mozilla Rhino).

On Node.js you have to connect your text diff/patch library explicitly. eg:

var jsondiffpatch = require('./jsondiffpatch.js');

// load google diff_match_patch library for text diff/patch
jsondiffpatch.config.diff_match_patch = require('./diff_match_patch_uncompressed.js');

// use text diff for strings longer than 5 chars
jsondiffpatch.config.textDiffMinLength = 5;

var d = jsondiffpatch.diff({ age: 5, name: 'Arturo' }, {age: 7, name: 'Armando' });
// d = {
//   age: [ 5, 7 ],
//   name: [ '@@ -1,6 +1,7 @@\n Ar\n-tur\n+mand\n o\n', 0, 2 ] }

console.log(d.name[0])
// prints:
// @@ -1,6 +1,7 @@
// Ar
// -tur
// +mand
//  o

Visual Diff

To visualize diffs you can include JsonDiffPatch.Html script + css on your page:

<script type="text/javascript" src="/path/to/jsondiffpatch.html.js"></script>
<link rel="stylesheet" href="../src/jsondiffpatch.html.css" type="text/css" />

Now you can use the jsondiffpatch.html.diffToHtml() function to visualize diffs as html:

    var json1 = JSON.parse($('#json1').val());
    var json2 = JSON.parse($('#json2').val());
    var d = jsondiffpatch.diff(json1, json2);
    $('#myvisualdiffcontainer').empty().append(jsondiffpatch.html.diffToHtml(json1, json2, d));

To see this in action check the DEMO page.

Also you can generate diffs with jsondiffpatch on your console:

jsondiffpatch .\test\testdata.json .\test\testdata2.json

About

Diff & Patch for JavaScript object trees.

Resources

License

Stars

Watchers

Forks

Packages

No packages published