JavaScript: JSON diff

Introduction

In this tutorial we will learn how to use the jsondiffpatch library to detect the difference between two JavaScript objects.

You can install the library with npm by sending the following command:

npm install jsondiffpatch

In this introductory tutorial we will cover simpler use cases such as comparing simple objects that contain textual, numeric and Boolean properties. After that we will also cover some slightly more complex use cases but based on these property types.

You can check more information about the JSON deltas format (the difference between the JSONs) here.

JSON Diff on text properties

We will start by using the create function from the jsondiffpatch package to create an object of class DiffPatcher, which we will use to perform the diffs.

const jsondiffpatch = require('jsondiffpatch').create();

Then we will define a very simple JavaScript object that contains a property called name, which contains a string (“test”).

const person = {
    name : "test",
};

We will create a second object containing the same property name, but now with a different string (“test 2”). Naturally this means that, if we compare both objects, they will differ on the name property.

const person2 = {
    name : "test 2",
};

To perform the diff, we simply need to call the diff method on the DiffPatcher object, passing as first input the first object and as second input the second object.

As output, this method call will return a JavaScript object that contains the diff.

const diff = jsondiffpatch.diff(person, person2);

This diff object will be in the following format:

{ changedPropertyName: [ 'old value', 'new value' ] }

So, it will contain the name of the property that has a difference and it will correspond to an array where the first element is the old value (from the first JSON passed to the diff method) and the second element is the new value (from the second JSON passed to the diff method).

So, applied to our example, we expect the following:

{ name: [ 'test', 'test 2' ] }

You can check the full code to test below.

const jsondiffpatch = require('jsondiffpatch').create();

const person = {
    name : "test",
};

const person2 = {
    name : "test 2",
};

const diff = jsondiffpatch.diff(person, person2);

console.log(diff);

After running it in a tool of your choice (in my case I’m using Visual Studio Code with the Code Runner extension) you should get a result similar to figure 1.

As can be seen, the diff object has a property called “name“, like we had in our objects, and the array contains the old value and the new value, as expected.

Output of the program, showing the JSON diff between the objects with a textual property.
Figure 1 – Output of the program, showing the diff between the objects.

Important: the library uses google-diff-match-patch for long text diffs (difference at character level) [1], when the property value is bigger than 60 characters in both JSON objects [2].

This default value of 60 characters can be changed by passing an object with options on the creation of the DiffPatcher object, as can be seen here.

At the time of writing I haven’t found a way of explicitly disabling the application of this character level diffing in text properties, but setting the minLength of the textDiff option to Number.MAX_VALUE seems to work fine as workaround.

Diff on numeric properties

We will now check what is the behavior when we are diffing properties with numbers. We will do exactly the same as before but our person objects will contain a property called age. The property will contain a number.

The full code can be seen below.

const jsondiffpatch = require('jsondiffpatch').create();

const person = {
    age : 20,
};

const person2 = {
    age : 30,
};

const diff = jsondiffpatch.diff(person, person2);

console.log(diff);

In short, the diff object that is returned by diffing the two JSON objects will have the same structure as before: it will contain a property called age which corresponds to an array with the old value and the new value. You can confirm this result on figure 2 below.

Output of the program, showing the JSON diff between the objects with a numeric property.
Figure 2 – Output of the program, showing the diff between the objects with a numeric property.

Diff on Boolean properties

For completion, we will also analyze the behavior of the diffing on Boolean properties. We will follow the same code structure as before, but now using a Boolean property called married on our objects

const jsondiffpatch = require('jsondiffpatch').create();

const person = {
    married : true,
};

const person2 = {
    married : false,
};

const diff = jsondiffpatch.diff(person, person2);

console.log(diff);

As can be seen in figure 3, the structure of the result is the same: the property name is used in the resulting diff object and it contains the old and new values.

Output of the program, showing the JSON diff between the objects with a Boolean property.
Figure 3Output of the program, showing the diff between the objects with a Boolean property.

More complex examples

To finalize this introductory tutorial, we will check some more complex use cases that can occur. We will cover the following scenarios:

  • noDiffProperty: A property that won’t change its value between the two objects;
  • explicitNullProperty: A property that will be explicitly set to null on the second object (note that this is different from the property not being present in the object);
  • propertyWithTypeChange: A property that changes its type from a number to a string;
  • propertyThatWillBeDeleted: A property that appears on the first object but not on the second;
  • newProperty: A property that doesn’t appear on the first object but appears on the second.

Like before, we start by creating the DiffPatcher object.

const jsondiffpatch = require('jsondiffpatch').create();

Then we will define the two JavaScript objects with the properties we need to cover the use cases listed before.

const person = {
    noDiffProperty: "test",
    explicitNullProperty: "some content",
    propertyWithTypeChange: 10,
    propertyThatWillBeDeleted: "content"
};

const person2 = {
    noDiffProperty: "test",
    explicitNullProperty: null,
    propertyWithTypeChange: "a string",
    newProperty: "something"
};

To finalize, we will apply the diff and print the result to the console. The complete code can be seen below.

Note that, since we have multiple properties with differences, the diff object will also contain multiple properties.

const jsondiffpatch = require('jsondiffpatch').create();

const person = {
    noDiffProperty: "test",
    explicitNullProperty: "some content",
    propertyWithTypeChange: 10,
    propertyThatWillBeDeleted: "content"
};

const person2 = {
    noDiffProperty: "test",
    explicitNullProperty: null,
    propertyWithTypeChange: "a string",
    newProperty: "something"
};

const diff = jsondiffpatch.diff(person, person2);

console.log(diff);

You can check the result below at figure 4.

Output of the program, showing the diffs between the properties of the objects.
Figure 4 – Output of the program, showing the diffs between the properties of the objects.

As can be seen, for each use case we have the following result:

  • noDiffProperty: Since the property did not change its value between the two JSON objects, then this property doesn’t appear in the diff object;
  • explicitNullProperty: The property still contains the array with old value and new value and the new value simply corresponds to null;
  • propertyWithTypeChange: The property still contains the array with old value and new value. The old value is a number and the new value is a string;
  • propertyThatWillBeDeleted: The property will correspond to an array with 3 elements. The first position contains the old value and the second and third positions contain the value 0;
  • newProperty: The property will contain an array with a single element that corresponds to the new value.

Suggested readings

References

[1] https://github.com/benjamine/jsondiffpatch

[2] https://github.com/benjamine/jsondiffpatch#options

Leave a Reply