Git Product home page Git Product logo

jsondiffpatch's Introduction

jsondiffpatch

Build Status Code Climate Test Coverage NPM version NPM dependencies

Diff & patch JavaScript objects


  • min+gzipped ~ 16KB
  • browser and server (ESM-only)
  • (optionally) uses google-diff-match-patch for long text diffs (diff at character level)
  • smart array diffing using LCS, IMPORTANT NOTE: to match objects inside an array you must provide an objectHash function (this is how objects are matched, otherwise a dumb match by position is used). For more details, check Array diff documentation
  • reverse a delta
  • unpatch (eg. revert object to its original state using a delta)
  • simplistic, pure JSON, low footprint delta format
  • multiple output formatters:
    • html (check it at the Live Demo)
    • annotated json (html), makes the JSON delta format self-explained
    • console (colored), try running ./node_modules/.bin/jsondiffpatch left.json right.json
    • JSON Patch format RFC 6902 support
    • write your own! check Formatters documentation
  • BONUS: jsondiffpatch.clone(obj) (deep clone)

Supported platforms

  • Any browser that supports ES6
  • Node.js 18, 20+

Usage

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

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

// make some changes
country2.name = 'Republica Argentina';
country2.population = 41324992;
delete country2.capital;

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

assertSame(delta, {
  name: ['Argentina', 'Republica Argentina'], // old value, new value
  population: ['41324992'], // new value
  capital: ['Buenos Aires', 0, 0], // deleted
});

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

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

const delta2 = jsondiffpatch.diff(country, country2);
assert(delta2 === undefined);
// undefined => no difference

Array diffing:

// sample data
const country = {
  name: 'Argentina',
  cities: [
    {
      name: 'Buenos Aires',
      population: 13028000,
    },
    {
      name: 'Cordoba',
      population: 1430023,
    },
    {
      name: 'Rosario',
      population: 1136286,
    },
    {
      name: 'Mendoza',
      population: 901126,
    },
    {
      name: 'San Miguel de Tucuman',
      population: 800000,
    },
  ],
};

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

// delete Cordoba
country.cities.splice(1, 1);

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

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

// create a configured instance, match objects by name
const diffpatcher = jsondiffpatch.create({
  objectHash: function (obj) {
    return obj.name;
  },
});

const delta = diffpatcher.diff(country, country2);

assertSame(delta, {
  cities: {
    _t: 'a', // indicates this node is an array (not an object)
    1: [
      // inserted at index 1
      {
        name: 'Cordoba',
        population: 1430023,
      },
    ],
    2: {
      // population modified at index 2 (Rosario)
      population: [1137520, 1136286],
    },
    _3: [
      // removed from index 3
      {
        name: 'La Plata',
      },
      0,
      0,
    ],
    _4: [
      // move from index 4 to index 2
      '',
      2,
      3,
    ],
  },
});

For more example cases (nested objects or arrays, long text diffs) check packages/jsondiffpatch/test/examples/

If you want to understand deltas, see delta format documentation

Installing

NPM

This works for node, or in browsers if you already do bundling on your app

npm install jsondiffpatch
import * as jsondiffpatch from 'jsondiffpatch';
const jsondiffpatchInstance = jsondiffpatch.create(options);

browser

In a browser, you can load a bundle using a tool like esm.sh or Skypack.

Options

import * as jsondiffpatch from 'jsondiffpatch';

// Only import if you want text diffs using diff-match-patch
import DiffMatchPatch from 'diff-match-patch';

const jsondiffpatchInstance = jsondiffpatch.create({
  // used to match objects when diffing arrays, by default only === operator is used
  objectHash: function (obj) {
    // this function is used only to when objects are not equal by ref
    return obj._id || obj.id;
  },
  arrays: {
    // default true, detect items moved inside the array (otherwise they will be registered as remove+add)
    detectMove: true,
    // default false, the value of items moved is not included in deltas
    includeValueOnMove: false,
  },
  textDiff: {
    // If using text diffs, it's required to pass in the diff-match-patch library in through this proprty.
    // Alternatively, you can import jsondiffpatch using `jsondiffpatch/with-text-diffs` to avoid having to pass in diff-match-patch through the options.
    diffMatchPatch: DiffMatchPatch,
    // default 60, minimum string length (left and right sides) to use text diff algorythm: google-diff-match-patch
    minLength: 60,
  },
  propertyFilter: function (name, context) {
    /*
       this optional function can be specified to ignore object properties (eg. volatile data)
        name: property name, present in either context.left or context.right objects
        context: the diff context (has context.left and context.right objects)
      */
    return name.slice(0, 1) !== '$';
  },
  cloneDiffValues: false /* default false. if true, values in the obtained delta will be cloned
      (using jsondiffpatch.clone by default), to ensure delta keeps no references to left or right objects. this becomes useful if you're diffing and patching the same objects multiple times without serializing deltas.
      instead of true, a function can be specified here to provide a custom clone(value)
      */,
});

Visual Diff

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="./style.css" type="text/css" />
    <link
      rel="stylesheet"
      href="https://esm.sh/[email protected]/lib/formatters/styles/html.css"
      type="text/css"
    />
    <link
      rel="stylesheet"
      href="https://esm.sh/[email protected]/lib/formatters/styles/annotated.css"
      type="text/css"
    />
  </head>
  <body>
    <div id="visual"></div>
    <hr />
    <div id="annotated"></div>
    <script type="module">
      import * as jsondiffpatch from 'https://esm.sh/[email protected]';
      import * as annotatedFormatter from 'https://esm.sh/[email protected]/formatters/annotated';
      import * as htmlFormatter from 'https://esm.sh/[email protected]/formatters/html';

      const left = { a: 3, b: 4 };
      const right = { a: 5, c: 9 };
      const delta = jsondiffpatch.diff(left, right);

      // beautiful html diff
      document.getElementById('visual').innerHTML = htmlFormatter.format(
        delta,
        left,
      );

      // self-explained json
      document.getElementById('annotated').innerHTML =
        annotatedFormatter.format(delta, left);
    </script>
  </body>
</html>

To see formatters in action check the Live Demo.

For more details check Formatters documentation

Console

# diff two json files, colored output (using chalk lib)
./node_modules/.bin/jsondiffpatch ./docs/demo/left.json ./docs/demo/right.json

# or install globally
npm install -g jsondiffpatch

jsondiffpatch ./docs/demo/left.json ./docs/demo/right.json

console_demo!

Plugins

diff(), patch() and reverse() functions are implemented using Pipes & Filters pattern, making it extremely customizable by adding or replacing filters on a pipe.

Check Plugins documentation for details.

jsondiffpatch's People

Contributors

arjunvegda avatar beneidel avatar benjamine avatar bomgar avatar dayures avatar emmenko avatar ian-craig avatar ilyaryabchinski avatar jacobq avatar johnrees avatar jonthemiller avatar kohei-takata avatar leinster avatar lholmquist avatar marcosrjjunior avatar mattdarveniza avatar methuselah96 avatar mrdick47 avatar nchen63 avatar nikvdp avatar pauljm avatar peterdavehello avatar piomar123 avatar plan-do-break-fix avatar raphinesse avatar rexxars avatar schnerd avatar sputh avatar xamgore avatar yukidarake avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsondiffpatch's Issues

Demo won't load examples in Firefox

Demos won't load after changing middle drop-down in Win7 Firefox 24 fork (PaleMoon) & Firefox Aurora 30a2. They work fine in Chrome 33.
Perhaps Firefox changed XHR policies?
TIA!

Array diff loses results

There appears to be a bug in the array diff code as it seems to lose results. For example:

Left:

[
    "10.13.13.70",
    "10.13.21.168",
    "10.13.4.137"
]

Right:

[
    "10.13.13.70",
    "10.13.13.71",
    "10.13.21.168",
    "10.13.4.137"
]

Result:

[
    "10.13.13.70",    // Unchanged
    "10.13.13.71",    // Added
    "10.13.21.168",   // ?? Lost
    "10.13.4.137"     // Unchanged
]

I checked with multiple formatters and I saw the same issue with all of them

Patch behavior when dealing with references

This is more a discussion than a bug or a feature request.

In my code I'm constantly diffing and patching two objects and I noticed the following behavior:

var obj1 = {};
var obj2 = { arr: [{a:1}]};

var diff1 = jsondiffpatch.diff(obj1, obj2);
console.log('first diff:', diff1);

// patch the first object
jsondiffpatch.patch(obj1, diff1);

// change the object inside the array on object 2
obj2.arr[0].a = 2;

var diff2 = jsondiffpatch.diff(obj1, obj2);
console.log(diff2) // -> undefined

Run on JSFiddle: http://jsfiddle.net/janmonschke/F3qqL/

I was expecting the second diff to contain the change of the firs element in the array, but I got an empty diff. I guess it's because the patch method copies over references instead of (a copy of) the object.

Is this behavior intended?

Since I'm only dealing with very small diffs in my application I simply create a deep copy of the diff before I patch the object. Which could be a general solution to the problem but I'm not sure how well that performs when applications are dealing with larger diffs.

Error loading diff_match_patch_uncompressed

Thanks for the awesome tool!

When the length of my diff exceeds the minimum length, the getDiffMatchPatch() call fails with the following error:

Error: Cannot find module '../../external/diff_match_patch_uncompressed'

I can confirm that this file exists under bower_components/jsondiffpatch/external/.

License?

I'd like to use this in a project I'm working on but it's not clear how this is licensed. Any help would be appreciated!

Method to detect conflicts?

Hi, I'm very interested in using JsonDiffPatch, but can't find how to detect and spell out conflicts between patches. That is, I'm looking for a function that, given the original data and a list of patches, spits out any unresolvable conflicts between the patches.

Does such a function exist, or would it be easy to write? Thanks

Document make process

I'm sure I'm super-dumb but I don't know how to do the make or gulp processes. Tried typing make in the directory but that did not work. Don't know gulp or fiberglas.

How about either (i) giving explicit build instructions, or (ii) just putting the built js files into github?

deletion from array

Hi benjamine,

I'm working on an application where there is need for simultaneous collaboration.
We use jsondiffpatch, but we have a problem:

We have a datastructure that looks like a tree, we use arrays to store the children of nodes. Each node also has a unique ID. Now here is the problem: Suppose we delete a node that is on position 4 in the array, that produces a diff. This diff is send to the server.
At the same moment, an other user changes the name of the node on position 4 (it wasn't deletet in his version). And also sends his diff to the server. Now, the deletion is handled first, and node 5 becomes node 4, after that, de namechange is processed, the diff only says that the name of the node in 4th place has to change). So the former node 5 gets another name, but that's not our intention. Is there an easy solution to this problem? something where there is first an ID-controle? Or should i make a new plugin? I think it's a trivial problem that must have occured before.

Thanks!

Performance analysis of jsondiffpatch

Hello,

is there any known study of the library performance. I am using it for a project and i notice that starting 3000 nodes in a json array, the diff would takes 3 ,6 seconds... etc

Are there any performance tips to use. maybe a heuristic algorithm to prevent visiting nodes that would "not likely" unmatch ?

Thanks

Applying diff sets object key to undefined but does not delete

When applying a diff between an object v1 that has key k and an object v2 that doesn't have k to v1, the result has key k mapped to undefined. Instead, k should be deleted from the object completely.

var v1 = { "k": 1 };
var v2 = {};

var diff = jsondiffpatch.diff(v1, v2);
var patched = jsondiffpatch.patch(v1, diff);

console.log(Object.keys(patched)); // [ 'k' ]

delete patched.k;

console.log(Object.keys(patched)); // []

Diff for Array of Objects fails

When we try to make diff for array of objects, even a copy of same object shows as removed and newly inserted

var obj1 = {
    'test': {
        'list': [
            {'a': 1},
            {'a': 2},
            {'a': 3}
        ]
    }
};

var obj2 = {
    'test': {
        'list': [
            {'a': 1},
            {'a': 2},
            {'a': 3}
        ]
    }
};

var delta = jsondiffpatch.diff(hotel1, hotel2);
console.log(delta);

Gives the result as all the items are removed and added again.

Object expected error from generated delta on array with object with arrays

I tried to boil this down to the simplest code possible, but basically I'm just deleting a line from my object, generating a delta, then running unpatch to revert it to its' original state. That's when I get the error: cannot apply patch at "//lines/0/sellingTitle/packages/0": object expected. Only seem to have this when it's that far embedded, the items being compared are deep cloned so the reference is not equal on them.

 var item =  {
   "lines":[
      {
         "sellingTitle":{
            "packages":[

            ]
         }
      },
      {
         "sellingTitle":{
            "packages":[

            ]
         }
      }
   ]
}
  var delta = {
   "lines":{
      "0":{
         "sellingTitle":{
            "packages":{
               "0":{
                  "sellingTitleId":[
                     26299,
                     26300
                  ],
                  "distributionPercent":[
                     100,
                     0
                  ]
               },
               "_t":"a"
            },
            "packageMembers":[
               [

               ],
               0,
               0
            ]
         },
         "packageMembers":[
            [

            ],
            0,
            0
         ]
      },
      "1":{
         "sellingTitle":{
         }
      },
      "_t":"a"
   }
};

jsondiffpatch.unpatch(item, delta);

Unchanged Values in demo wrong, order of array operations question

Hi just noted this while trying to get a grasp on demo's visualization:

If you are in Visual mode (default example) here http://benjamine.github.io/jsondiffpatch/demo/index.html and flip "Show unchanged values" checkbox it then shows name: "Uruguay" for index 10 as if population is added to Uruguay when it in fact is added to moved "Colombia"

screen shot 2014-10-30 at 10 01 38 am

This may be just visualization issue though.

Meanwhile trying to understand what order move / remove / insert / update operations in array are executed..

Thanks in advance, the latter could be added to documentation!
Great work!

case sensitivity

Is it possible to have the comparison be case (in)sensitive, maybe based on an addition parameter?

Parsing diff text

Is there a parser for diff language? I mean this:

"@@ -618,17 +618,17 @@\n via, Bra\n-z\n+s\n il, Chil\n@@ -886,20 +886,13 @@\n re a\n-lso known as\n+.k.a.\n Car\n",

I want to incrementally show/hide text changes in textarea.

Thanks!
Bartek

Support RegExp objects

The JSON.stringify method treats RegExp objects as {}, leading to results like below:

jsondiffpatch.diff( {}, /regex/ ); // => undefined
jsondiffpatch.diff( /someRegex/, /someOtherRegex/ ); // => undefined

I understand that RegExp objects don't serialize to JSON, but I'm wondering if, in some contexts, it would make sense for it to return (in the first example):

[ '{}', /regex/ ] 

This would involve a bit more processing for RegExp objects beyond JSON.stringify, but I'm willing to write up a PR if you think that it's worth merging.

Inserted Array Item

Hi

I was wondering if jsondiffpatch.diff([1,2,3],[0,1,2,3]) coud give a more efficient patch? Rather than resulting in 3 items being changed and one being append, e,g:

"{"0":[1,0],"1":[2,1],"2":[3,2],"3":[3],"_t":"a"}"

Could it result in just on item being prepended, e.g:
"{"0":[0]}"

Regards
Adam

Getting some diff output with identical files

When I diff this file against itself I get some output.

{
  "Images" : {
    "image[0]" : {
      "fieldset" : "Primary Image",
      "type" : "Image",
      "config" : {
        "constraint" : {
          "width" : 945.0,
          "height" : 450.0
        },
        "thumbnails" : [ {
          "name" : "Standard",
          "width" : 390.0,
          "height" : 246.0
        }, {
          "name" : "Featured",
          "width" : 630.0,
          "height" : 466.0
        } ]
      }
    }
  }
}

I get the following output:

{
  Images: {
    image[0]: {
      config: {
        thumbnails: [
          0: {
            "name": "Standard",
            "width": 390,
            "height": 246
          }
          0: {
            "name": "Standard",
            "width": 390,
            "height": 246
          }
          1: {
            "name": "Featured",
            "width": 630,
            "height": 466
          }
          1: {
            "name": "Featured",
            "width": 630,
            "height": 466
          }
        ]
      }
    }
  }
}

With the first 0 and the first 1 in red, and the second 0 and the second 1 in green.

screen shot 2014-06-19 at 4 45 06 pm

'Moved' to same position

If I'm understanding the docs correctly, it wouldn't make sense for a diff to contain _2: [ "", 2, 3], as that would mean that what was once at position 2 is now at position 2. In other words, it hasn't moved.

I'm getting that value with the following diff:

var a = [
  'one',
  'zero',
  'two',
];

var aa = [
  'five',
  'four',
  'two',
  'one'
];

jsondiffpatch.diff( a, aa );

Array move diff returned as remove + insert in new version

Hi, I've been playing with the new version of the library and I found a case where diffing two arrays doesn't detect a move.

  • Data : [1, 2, 3, 4] and [2, 4, 1, 3]

  • diff result with the current version of the library :

    {
      "1": [
        4
      ],
      "_t": "a",
      "_1": [
        "",
        0,
        3
      ],
      "_3": [
        4,
        0,
        0
      ]
    }
    
  • diff with the 0.0.11 version :

    {
      "_t": "a",
      "_1": [
        "",
        0,
        3
      ],
      "_3": [
        "",
        1,
        3
      ]
    }
    

Unpatch for deep arrays

Attempting to unpatch with diff with deep arrays.

Uncaught TypeError: Cannot read property 'tocLabel' of undefined

This has deep arrays which you may not be handling. I tested against the latest code.

  var instance = jsondiffpatch.create({
    objectHash: function (obj) {
      return obj._id || obj.id || obj.name || JSON.stringify(obj);
    },
    arrays: {
      detectMove: true,
      includeValueOnMove: false
    },
    textDiff: {
      minLength: 60
    }
  });

Here is my left:

{
  "_id" : "548b4f14462e2c102629e011",
  "docSections" : [
    {
      "section" : "53a6778b7192aa7831aca40b",
      "_id" : "548b4f14462e2c102629e027",
      "showTitle" : false,
      "docContents" : [
        {
          "content" : "5385011f6c0882c424b89159",
          "title" : "Title",
          "body" : "<div style=\"text-align: center;\">Catchy Title</div>",
          "_id" : "548b4f14462e2c102629e029",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "1.1",
          "pageBreak" : false,
          "showTitle" : false
        }
      ],
      "tocLabel" : "1",
      "isOpen" : false,
      "title" : "Cover Page"
    },
    {
      "section" : "53a6798ab0ea78f02f311e03",
      "_id" : "548b4f14462e2c102629e024",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "53961edaeff90574291f17dc",
          "title" : "Table of Contents",
          "body" : "<p><span>[[Table of Contents]]</span></p>",
          "_id" : "548b4f14462e2c102629e026",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "2.1",
          "pageBreak" : false,
          "showTitle" : false
        }
      ],
      "tocLabel" : "2",
      "isOpen" : true,
      "title" : "Table of Contents"
    },
    {
      "section" : "53a684f2dea5d3ec318ac670",
      "_id" : "548b4f14462e2c102629e01a",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "5385129a516a7dd82a764102",
          "title" : "Other Considerations",
          "body" : "<p><span>Consider this content</span><br/></p>&#10;",
          "_id" : "548b4f14462e2c102629e01d",
          "docContents" : [
            {
              "content" : "53851216516a7dd82a764101",
              "title" : "Contents of Document",
              "body" : "<p>Document Contents</p>",
              "_id" : "548b4f14462e2c102629e01c",
              "docContents" : [],
              "comments" : [],
              "tocLabel" : "3.1.1",
              "pageBreak" : false,
              "showTitle" : true
            }
          ],
          "comments" : [],
          "tocLabel" : "3.1",
          "pageBreak" : false,
          "showTitle" : true
        }
      ],
      "tocLabel" : "3",
      "isOpen" : true,
      "title" : "Body"
    },
    {
      "section" : "53a679eab0ea78f02f311e04",
      "_id" : "548b4f14462e2c102629e01e",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "53851110516a7dd82a7640ff",
          "title" : "Introduction part",
          "body" : "<p>Break it down</p>",
          "_id" : "548b4f14462e2c102629e023",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "4.1",
          "pageBreak" : false,
          "showTitle" : true
        },
        {
          "content" : "53850c8921b0605c244fa0bd",
          "title" : "Contact Information",
          "body" : "<p>Contact Information</p>",
          "_id" : "548b4f14462e2c102629e022",
          "docContents" : [
            {
              "content" : "53850bbecf996bc81309d5ed",
              "title" : "Who, What, Why, When and How",
              "body" : "<p>The five questions</p>",
              "_id" : "548b4f14462e2c102629e021",
              "docContents" : [],
              "comments" : [],
              "tocLabel" : "4.2.1",
              "pageBreak" : false,
              "showTitle" : true
            },
            {
              "showTitle" : true,
              "pageBreak" : false,
              "tocLabel" : "4.2.2",
              "comments" : [],
              "docContents" : [],
              "_id" : "548b4f14462e2c102629e020",
              "body" : "<p>Hello</p>&#10;",
              "title" : "Introduction Statement",
              "content" : "53850b03cf996bc81309d5ec"
            }
          ],
          "comments" : [],
          "tocLabel" : "4.2",
          "pageBreak" : false,
          "showTitle" : true
        }
      ],
      "tocLabel" : "4",
      "isOpen" : true,
      "title" : "Introduction"
    },
    {
      "section" : "53a68502dea5d3ec318ac671",
      "_id" : "548b4f14462e2c102629e017",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "539b597e22a7fa94178653f4",
          "title" : "Appendix",
          "body" : "Appendix",
          "_id" : "548b4f14462e2c102629e019",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "5.1",
          "pageBreak" : true,
          "showTitle" : true
        }
      ],
      "tocLabel" : "5",
      "isOpen" : false,
      "title" : "Appendix"
    }
  ],
  "description" : "test",
  "title" : "test"
}

Here is my right:


{
"_id" : "548b4f14462e2c102629e011",
"docSections" : [
{
"section" : "53a6778b7192aa7831aca40b",
"_id" : "548b4f14462e2c102629e027",
"showTitle" : false,
"docContents" : [
{
"content" : "5385011f6c0882c424b89159",
"title" : "Title",
"body" : "<div style=\"text-align: center;\">Catchy Title</div>",
"_id" : "548b4f14462e2c102629e029",
"docContents" : [],
"comments" : [],
"tocLabel" : "1.1",
"pageBreak" : false,
"showTitle" : false
}
],
"tocLabel" : "1",
"isOpen" : false,
"title" : "Cover Page"
},
{
"section" : "53a6798ab0ea78f02f311e03",
"_id" : "548b4f14462e2c102629e024",
"showTitle" : true,
"docContents" : [
{
"content" : "53961edaeff90574291f17dc",
"title" : "Table of Contents",
"body" : "<p><span>[[Table of Contents]]</span></p>",
"_id" : "548b4f14462e2c102629e026",
"docContents" : [],
"comments" : [],
"tocLabel" : "2.1",
"pageBreak" : false,
"showTitle" : false
}
],
"tocLabel" : "2",
"isOpen" : true,
"title" : "Table of Contents"
},
{
"section" : "53a68502dea5d3ec318ac671",
"_id" : "548b4f14462e2c102629e017",
"showTitle" : true,
"docContents" : [
{
"content" : "539b597e22a7fa94178653f4",
"title" : "Appendix",
"body" : "Appendix",
"_id" : "548b4f14462e2c102629e019",
"docContents" : [],
"comments" : [],
"tocLabel" : "3.1",
"pageBreak" : true,
"showTitle" : true
}
],
"tocLabel" : "3",
"isOpen" : false,
"title" : "Appendix"
},
{
"section" : "53a684f2dea5d3ec318ac670",
"_id" : "548b4f14462e2c102629e01a",
"showTitle" : true,
"docContents" : [
{
"content" : "5385129a516a7dd82a764102",
"title" : "Other Considerations",
"body" : "<p><span>Consider this content</span><br/></p>&#10;",
"_id" : "548b4f14462e2c102629e01d",
"docContents" : [
{
"content" : "53851216516a7dd82a764101",
"title" : "Contents of Document",
"body" : "<p>Document Contents</p>",
"_id" : "548b4f14462e2c102629e01c",
"docContents" : [],
"comments" : [],
"tocLabel" : "4.1.1",
"pageBreak" : false,
"showTitle" : true
}
],
"comments" : [],
"tocLabel" : "4.1",
"pageBreak" : false,
"showTitle" : true
}
],
"tocLabel" : "4",
"isOpen" : true,
"title" : "Body"
},
{
"section" : "53a679eab0ea78f02f311e04",
"_id" : "548b4f14462e2c102629e01e",
"showTitle" : true,
"docContents" : [
{
"content" : "53851110516a7dd82a7640ff",
"title" : "Introduction part",
"body" : "<p>Break it down</p>",
"_id" : "548b4f14462e2c102629e023",
"docContents" : [],
"comments" : [],
"tocLabel" : "5.1",
"pageBreak" : false,
"showTitle" : true
},
{
"content" : "53850c8921b0605c244fa0bd",
"title" : "Contact Information",
"body" : "<p>Contact Information</p>",
"_id" : "548b4f14462e2c102629e022",
"docContents" : [
{
"content" : "53850bbecf996bc81309d5ed",
"title" : "Who, What, Why, When and How",
"body" : "<p>The five questions</p>",
"_id" : "548b4f14462e2c102629e021",
"docContents" : [],
"comments" : [],
"tocLabel" : "5.2.1",
"pageBreak" : false,
"showTitle" : true
},
{
"showTitle" : true,
"pageBreak" : false,
"tocLabel" : "5.2.2",
"comments" : [],
"docContents" : [],
"_id" : "548b4f14462e2c102629e020",
"body" : "<p>Hello GoodBye</p>&#10;",
"title" : "Introduction Statement",
"content" : "53850b03cf996bc81309d5ec"
}
],
"comments" : [],
"tocLabel" : "5.2",
"pageBreak" : false,
"showTitle" : true
}
],
"tocLabel" : "5",
"isOpen" : true,
"title" : "Appendix"
}
],
"description" : "test",
"title" : "test"
}

Here is the diff that is created and when trying to unpatch I get the error:

{
"docSections": {
"2": {
"docContents": {
"0": {
"tocLabel": [
"5.1",
"3.1"
]
},
"_t": "a"
},
"tocLabel": [
"5",
"3"
]
},
"3": {
"docContents": {
"0": {
"docContents": {
"0": {
"tocLabel": [
"3.1.1",
"4.1.1"
]
},
"_t": "a"
},
"tocLabel": [
"3.1",
"4.1"
]
},
"_t": "a"
},
"tocLabel": [
"3",
"4"
]
},
"4": {
"docContents": {
"0": {
"tocLabel": [
"4.1",
"5.1"
]
},
"1": {
"docContents": {
"0": {
"tocLabel": [
"4.2.1",
"5.2.1"
]
},
"1": {
"tocLabel": [
"4.2.2",
"5.2.2"
],
"body": [
"<p>Hello</p>&#10;",
"<p>Hello GoodBye</p>&#10;"
]
},
"_t": "a"
},
"tocLabel": [
"4.2",
"5.2"
]
},
"_t": "a"
},
"tocLabel": [
"4",
"5"
],
"title": [
"Introduction",
"Appendix"
]
},
"_t": "a",
"_4": [
"",
2,
3
]
}
}

Thanks for taking the time to help debug.

Problem with nested objects in arrays?

jsondiffpatch doesn't seem to get a proper diff from nested objects in arrays.

Example:

{"a":"A","num":3,"projects":[{"arr":[{"h":"h","arr2":[{"metadata":{"names":true},"names":["name1","name2"]},{"metadata":{"names":true},"names":["name1","name2"]}]},{"h":"h2","arr2":[{"metadata":{"names":true},"names":["name3","name4"]},{"metadata":{"names":true},"names":["name3","name4"]}]}],"obj":{"visible":false},"str":"str"}]}

And

{"a":"B","num":4,"projects":[{"arr":[{"h":"h","arr2":[{"metadata":{"names":true},"names":["name1 & name 0","name2"]},{"metadata":{"names":true},"names":["name1 for testing","name2"]}]},{"h":"h2","arr2":[{"metadata":{"names":true},"names":["name3","name4"]},{"metadata":{"names":true},"names":["name3","name4 being tested"]}]}],"obj":{"visible":true},"str":"str2"}]}

(I did configure the objectHash function)

better for give warnings when objectHash used but not defined

It took me quite long time to recognize this. I was diffing my data but kept getting long diffs. I had read the docs so I know something called objectHash, but I believe it has a default function to detect id and _id. What's more, as I went through the demo, I copied my code there and it was different from mine.

At last I looked into my code packed by browserify and, that default function is not there. Finally I got it.

So I'm suggesting give warnings when objectHash is not set. React does that in their DOM diffing, and it's quite friendly to new comers.

Feature Request... Diff Id's of objects updated

This is such a great library! I have one feature that I am considering implementing and maybe I could get some insight or support for this feature. I looked the through the code a bit but I am not exactly sure where to start? patch.js or array.js??

My JSON objects / child objects all have id's built in from Mongoose / Mongo and I would like to be able to retrieve the id's of any edited item by passing in the JSON object and a diff.

For instance if I had this JSON:

{
  "_id" : "548b4f14462e2c102629e011",
  "docSections" : [
    {
      "section" : "53a6778b7192aa7831aca40b",
      "_id" : "548b4f14462e2c102629e027",
      "showTitle" : false,
      "docContents" : [
        {
          "content" : "5385011f6c0882c424b89159",
          "title" : "Title",
          "body" : "<div style=\"text-align: center;\">Catchy Title</div>",
          "_id" : "548b4f14462e2c102629e029",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "1.1",
          "pageBreak" : false,
          "showTitle" : false
        }
      ],
      "tocLabel" : "1",
      "isOpen" : false,
      "title" : "Cover Page"
    },
    {
      "section" : "53a6798ab0ea78f02f311e03",
      "_id" : "548b4f14462e2c102629e024",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "53961edaeff90574291f17dc",
          "title" : "Table of Contents",
          "body" : "<p><span>[[Table of Contents]]</span></p>",
          "_id" : "548b4f14462e2c102629e026",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "2.1",
          "pageBreak" : false,
          "showTitle" : false
        }
      ],
      "tocLabel" : "2",
      "isOpen" : true,
      "title" : "Table of Contents"
    },
    {
      "section" : "53a684f2dea5d3ec318ac670",
      "_id" : "548b4f14462e2c102629e01a",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "5385129a516a7dd82a764102",
          "title" : "Other Considerations",
          "body" : "<p><span>Consider this content</span><br/></p>&#10;",
          "_id" : "548b4f14462e2c102629e01d",
          "docContents" : [
            {
              "content" : "53851216516a7dd82a764101",
              "title" : "Contents of Document",
              "body" : "<p>Document Contents</p>",
              "_id" : "548b4f14462e2c102629e01c",
              "docContents" : [],
              "comments" : [],
              "tocLabel" : "3.1.1",
              "pageBreak" : false,
              "showTitle" : true
            }
          ],
          "comments" : [],
          "tocLabel" : "3.1",
          "pageBreak" : false,
          "showTitle" : true
        }
      ],
      "tocLabel" : "3",
      "isOpen" : true,
      "title" : "Body"
    },
    {
      "section" : "53a679eab0ea78f02f311e04",
      "_id" : "548b4f14462e2c102629e01e",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "53851110516a7dd82a7640ff",
          "title" : "Introduction part",
          "body" : "<p>Break it down</p>",
          "_id" : "548b4f14462e2c102629e023",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "4.1",
          "pageBreak" : false,
          "showTitle" : true
        },
        {
          "content" : "53850c8921b0605c244fa0bd",
          "title" : "Contact Information",
          "body" : "<p>Contact Information</p>",
          "_id" : "548b4f14462e2c102629e022",
          "docContents" : [
            {
              "content" : "53850bbecf996bc81309d5ed",
              "title" : "Who, What, Why, When and How",
              "body" : "<p>The five questions</p>",
              "_id" : "548b4f14462e2c102629e021",
              "docContents" : [],
              "comments" : [],
              "tocLabel" : "4.2.1",
              "pageBreak" : false,
              "showTitle" : true
            },
            {
              "showTitle" : true,
              "pageBreak" : false,
              "tocLabel" : "4.2.2",
              "comments" : [],
              "docContents" : [],
              "_id" : "548b4f14462e2c102629e020",
              "body" : "<p>Hello</p>&#10;",
              "title" : "Introduction Statement",
              "content" : "53850b03cf996bc81309d5ec"
            }
          ],
          "comments" : [],
          "tocLabel" : "4.2",
          "pageBreak" : false,
          "showTitle" : true
        }
      ],
      "tocLabel" : "4",
      "isOpen" : true,
      "title" : "Introduction"
    },
    {
      "section" : "53a68502dea5d3ec318ac671",
      "_id" : "548b4f14462e2c102629e017",
      "showTitle" : true,
      "docContents" : [
        {
          "content" : "539b597e22a7fa94178653f4",
          "title" : "Appendix",
          "body" : "Appendix",
          "_id" : "548b4f14462e2c102629e019",
          "docContents" : [],
          "comments" : [],
          "tocLabel" : "5.1",
          "pageBreak" : true,
          "showTitle" : true
        }
      ],
      "tocLabel" : "5",
      "isOpen" : false,
      "title" : "Appendix"
    }
  ],
  "description" : "test",
  "title" : "test"
}

And this was the diff:

{
  "docSections": {
    "0": {
      "docContents": {
        "0": {
          "tocLabel": [
            "2.1",
            "1.1"
          ]
        },
        "_t": "a"
      },
      "tocLabel": [
        "2",
        "1"
      ]
    },
    "1": {
      "docContents": {
        "0": {
          "tocLabel": [
            "1.1",
            "2.1"
          ]
        },
        "_t": "a"
      },
      "tocLabel": [
        "1",
        "2"
      ]
    },
    "_t": "a",
    "_1": [
      "",
      0,
      3
    ]
  }
}

I would like to be able to retrieve something like this:
docSections["_id":"548b4f14462e2c102629e027","_id":"548b4f14462e2c102629e024"]
docContents["_id":"548b4f14462e2c102629e029","_id":"548b4f14462e2c102629e026"]

Any help would be greatly appreciated. Thanks

3-way merge

Is there a way to merge two diffs stemming from the same source JSON document? I think I saw some discussion about a desire to implement this but don't know if anything has materialized yet. Thanks!

Applying two or more patches to the same original object... possible?

Hello, I'm interested in using this library for real time data synchronization in a web app. This seems very doable with JsonDiffPatch, however I have a problem. Imagine two (or more) clients working on the same data. They both push patches to the server simultaneously on the same object. The problem is that I can't apply one patch to the object and expect the second to apply correctly as well. The way I imagine this working is to modify the second patch based on the first. Is this doable with this library? If you were in my position, how would you solve this problem?

Thanks for the help.

incorrect delta when an array has objects itself

Hi,
From the first look, I liked your library very much. But now, I am in trouble, trying the following code:

var json1 = {"a":[{"b":"c"}]};
var json2 = {"a":[{"b":"c"}]};

//json1 and json2 are both identical
var delta = jsondiffpatch.diff(json1, json2);

console.log(JSON.stringify(delta));

And I am getting the following output:

{"a":{"0":[{"b":"c"}],"_t":"a","_0":[{"b":"c"},0,0]}}

Two questions:

  1. is the above output correct? In my opinion, there should be no delta.
  2. How to read the delta? I didn't find any documentation to read delta. Please let me know if I am missing something. (what is _t stands for in delta?)

Any help is appreciated.

Ignore transient fields

Is there an easy way to configure JsonDiffPatch to ignore some of the fields when comparing two objects? I have a scenario where JSON data is loaded from a storage and then additional derived fields are added to it for internal use by the application. When running diff, I don't want them to be included in the patch.

Equal objects in array marked as added/deleted

sorry for my english
I think, that it is wrong result.
Left and right objects should be euqal.

var jsondiff = require('jsondiffpatch').jsondiffpatch.create({
        detectMove: true,
        includeValueOnMove: false
});
var left = {
    arr: [
        {key: 'val'}
    ]
};
var right = Object.clone(left, true); //sugarjs clone method
var delta = jsondiff.diff(left, right);
console.log(delta);
// prints: { arr: { '0': [ [Object] ], _t: 'a', _0: [ [Object], 0, 0 ] } }

Unexpected results in arrays with multiple changes

Tried here: http://benjamine.github.io/jsondiffpatch/demo/index.html
It's ok when there's only one change:

{
  "messages": [
    {
      "id": 1,
      "text": "a"
    },
    {
      "id": 0,
      "text": "a"
    }
  ]
}

changed to

{
  "messages": [
    {
      "id": 1,
      "text": "axx // changed here"
    },
    {
      "id": 0,
      "text": "a"
    }
  ]
}

results in

1

but when there are two changes, like changing to:

{  "messages": [
    {
      "id": 1,
      "text": "axx // changed here"
    },
    {
      "id": 0,
      "text": "axx // changed too"
    }
  ]
}

it retults in:

2

EPEERINVALID on npm install

npm ERR! peerinvalid The package karma does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9.3
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.12.8
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@~0.10.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9
npm ERR! peerinvalid Peer [email protected] wants karma@>=0.9

maybe casperjs issue?

Im gettings diffs that arent diffs... unless im reading it wrong

has this been tested with phantomJS/casperJS?

inverting patches

I'm wondering if there is a simple/obvious way to invert patches that I'm missing. Basically I want to be able to apply a patch and "un-apply" it too. Think "undo" and "redo". I suppose I could diff the json structures in reverse, but I was thinking that's a bit inefficient.

I just thought I would ask just in case you already know of an obvious solution.

Superfluous change detected for simple swap/reverse case

If you diff {"reverse": [1, 5, 9]} with {"reverse": [9, 5, 1]}, two changes are reported:

move from index 2 to index 0

correct.

move from index 1 to index 1

Why?!

Seems to happen if there is an odd number of elements only.

Visual diff seems to be incorrect when comparing very simple objects

diff2($("#visualdiff"), { a : [{a:1}]}, { a : [{a:1}]})

where

function diff2(el, config1, config2) {
    var diffs = jsondiffpatch.diff(config1, config2);
    el.empty();
    if (diffs) {
        el.append(jsondiffpatch.html.diffToHtml(config1, config2, diffs));
    } else {
        el.html("No diffs");
    }
}

Expected Results

No diffs.

Actual Results

jsondiffpatch-1

Not getting expected diff results

when diffing the following vars (coffeescript)

var map1 = [
    1,
    2,
    {val:3}
    ]
var map2 = [
    1,
    2,
    {val:4}
    ]

I get the total replacement of the object at index 2, instead of the changing of its "val" property

Error outputting diff when object in array changed while another is added before it

I've spotted a bug where the demo errors if you change an object in an array and add another object before it.

If you add the following JSON to the demo page you can see the error:

{
    "a": [{
        "id": 3,
        "title": "Third"
    }]
}
{
    "a": [
        {
            "id": 1,
            "title": "First"
        },
        {
            "id": 3,
            "title": "Third updated"
        }
    ]
}

This only occurs when jsondiffpatch.config.objectHash is a function which equates the objects by id (as it is on the demo page).

Need method to combine diffs

I collect and store a large graph of diffs that I would like to path & unpatch. My issue is I'd prefer to only patch / unpatch once and also have a way to combine diffs. I create my diffs on each 'substantial' change, so they can each be undone. Once a commit happens, I then want to squash all of those tiny diffs into 1 larger diff.

The library does not apply this patch with a moved subtree correctly

Hi,

I'm using JSONDiffPatch for an important project and I've run into a strange problem. With the input and output below, the library creates the correct patch. However, when you apply the patch to the input, the result is incorrect. The contents array is not completely emptied, even though the patch removes all the items.

Here's the original:

[
    {
        "contents": []
    },
    {
        "contents": [
            {
                "id": "abc"
            },
            {
                "id": "def"
            }
        ]
    }
]

Desired Output:

[
    {
        "contents": [
            {
                "id": "abc"
            },
            {
                "id": "def"
            }
        ]
    },
    {
        "contents": []
    }
]

Computed Diff (Correct):

{
  "0": {
    "contents": {
      "0": [
        {
          "id": "abc"
        }
      ],
      "1": [
        {
          "id": "def"
        }
      ],
      "_t": "a"
    }
  },
  "1": {
    "contents": {
      "0": [
        {
          "id": "abc"
        },
        0,
        0
      ],
      "1": [
        {
          "id": "def"
        },
        0,
        0
      ],
      "_t": "a"
    }
  },
  "_t": "a"
}

After applying the patch, we get this incorrect result:

[
    {
        "contents": [
            {
                "id": "abc"
            },
            {
                "id": "def"
            }
        ]
    },
    {
        "contents": [
            {
                "id": "def"
            }
    ]
    }
]

Unpatch fails for given scenario

Consider the following example:

    var src = {"parts":[{"id":"1","variation":{"x":"2","y":"0"},"distance":{"x":"3","y":"0"}}],"Id":"4"};
    var dst = {"parts":[{"id":"5","x":10},{"id":"6","variation":{"x":"3","y":"0"},"distance":{"x":"7","y":"0"}},{"id":"1","variation":{"x":"5","y":"0"},"distance":{"x":"3","y":"0"}},{"id":"8","variation":{"x":"2","y":"output0"},"distance":{"x":"5","y":"input0"}}],"Id":"4"};
    var diffpatcher = jsondiffpatch.create({
        objectHash: function (obj) {
            return obj._id || obj.id || obj.Id || JSON.stringify(obj);
        },
        arrays: {
            detectMove: true,
            includeValueOnMove: false
        },
        textDiff: {
            minLength: 60
        }
    });
    var delta = diffpatcher.diff(src, dst);
    diffpatcher.patch(src, delta);
    diffpatcher.unpatch(src, delta);

In this example patch works fine but unpatch fails with the following message: Uncaught TypeError: Cannot read property 'variation' of undefined

Diff between float and int is not displayed properly

In diffing 1.0 against 1 the output shows 1 in both red and green. They should either be treated as equivalent (I'm not sure if in JS they are technically equivalent or not) or displayed as float and int respectively.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.