Languages
[Edit]
EN

JavaScript - json difference visualization

7 points
Created by:
Violetd
775

In this article, we're going to have a look at how to write own function to compare and visualise JSONs. This article is composed with in two parts: json difference checking and table visualzisation. 

Simple preview:

{"name": "John", "items": [ 1 , 2, "3", {"name": "Item 1", "z": [4]       }, 5         ]}
{"name": "John", "items": ["1", 2,  3 , {"name": "Item 2",          "x": 3},   "a", "b"]}
//                          u       u                       d    d   c       d  c    c
// Where: u - updated, c - created, d - deleted
JSONs difference visualisation with table in JavaScript
JSONs difference visualisation with table in JavaScript

Note: read this article to see console print version.

Run below example to see details. 

1. Custom json difference visualization

This section presents simple logic that visualise difference of jsons in table.

// ONLINE-RUNNER:browser;

<!doctype html>
<html>
  <style>

    table { border-collapse: collapse; font-size: 13px; }

    td { padding: 2px 4px; border: 1px solid; }

    td.label { width: 40px; }
    td.value { min-width: 50px; }

    td.unchanged { border-color: #dddada; background: #ffffff; }
    td.old { border-color: #ffa8a8; background: #ffd6d6; }
    td.new { border-color: #92ca92; background: #e5ffe5; }

    td table { width: 100% }

  </style>
  <script>

    // -------------------------------------------------------------------
    // difference calculator

    function getType(object) {
      return Object.prototype.toString.call(object);
    }

    function getDescription(type) {
      if (type == '[object Array]') {
        return 'array';
      }
      if (type == '[object Object]') {
        return 'object';
      }
      return 'simple';
    }

    function createOldItem(name, status, type, value) {
      return {
        name: name || null,
        status: status,
        oldType: getDescription(type),
        newType: null,
        oldValue: value,
        newValue: null,
      };
    }

    function createNewItem(name, status, type, value) {
      return {
        name: name || null,
        status: status,
        oldType: null,
        newType: getDescription(type),
        oldValue: null,
        newValue: value,
      };
    }

    var createDeletedItem = function(key, type, value) {
      return createOldItem(key, 'deleted', type, value);
    };

    var createCreatedItem = function(key, type, value) {
      return createNewItem(key, 'created', type, value);
    };

    function coverContent(entity, createItem) {
      var items = [ ];

      for (const key in entity) {
        if (entity.hasOwnProperty(key)) {
          items.push(coverEntity(key, entity[key], createItem));
        }
      }

      return items;
    }

    function coverEntity(name, entity, createItem) {
      var type = getType(entity);

      if (type === '[object Array]' || type === '[object Object]') {
        var content = coverContent(entity, createItem);

        return createItem(name, type, content);
      } else {
        return createItem(name, type, entity);
      }
    }

    function compareContent(a, b) {
      var items = [ ];

      for (const key in a) {
        if (a.hasOwnProperty(key)) {
          if (b.hasOwnProperty(key)) {
            items.push(compareEntries(a[key], b[key], key));
          } else {
            items.push(coverEntity(key, a[key], createDeletedItem));
          }
        }
      }

      for (const key in b) {
        if (b.hasOwnProperty(key)) {
          if(a.hasOwnProperty(key)){
            continue;
          }

          items.push(coverEntity(key, b[key], createCreatedItem));
        }
      }

      return items;
    }

    function compareEntries(a, b, name) {
      var oldType = getType(a);
      var newType = getType(b);

      if (a === b) {
        return createOldItem(name, 'unchanged', oldType, a);
      }

      if (oldType == newType) {
        // both have array or object type:
        if (oldType === '[object Array]' || oldType === '[object Object]') {
          var oldValue = compareContent(a, b);

          return createOldItem(name, 'unchanged', oldType, oldValue);
        }
        // both have simple type: boolean, number, string, etc.:
        var description = getDescription(oldType);

        return {
          name: name || null,
          status: 'updated',
          oldType: description,
          newType: description,
          oldValue: a,
          newValue: b,
        };
      } else {
        // both have different types:
        var oldDescription = getDescription(oldType);
        var newDescription = getDescription(newType);

        return {
          name: name || null,
          status: 'updated',
          oldType: oldDescription,
          newType: newDescription,
          oldValue: (oldDescription == 'simple' ? a : coverContent(a, createDeletedItem)),
          newValue: (newDescription == 'simple' ? b : coverContent(b, createCreatedItem)),
        };
      }
    }

    // -------------------------------------------------------------------
    // difference visualization

    var rules = [
      { expression: /&/g, replacement: '&amp;'  }, // keep this rule at first position
      { expression: /</g, replacement: '&lt;'   },
      { expression: />/g, replacement: '&gt;'   },
      { expression: /"/g, replacement: '&quot;' },
      { expression: /'/g, replacement: '&#039;' }
    ];

    function escapeValue(value) {
      if(value == null) {
        return null;
      }

      var result = String(value);

      for (var i = 0; i < rules.length; ++i) {
        var rule = rules[i];

        result = result.replace(rule.expression, rule.replacement);
      }

      return result;
    };

    function createRow(type, name, value) {
      var html = '<tr class="' + type + '">';

      if (name) {
        html += '  <td class="label ' + type + '">' + name + '</td>';
      }

      html += '  <td class="value ' + type + '">' + value + '</td>';
      html += '</tr>';

      return html;
    }

    function createNode(node) {
      var html = '';
      var name = escapeValue(node.name);

      if (node.oldType) {
        var oldValue = (node.oldType == 'simple' ? escapeValue(node.oldValue) : createTable(node.oldValue));

        if (node.status == 'unchanged') {
          return createRow('unchanged', name, oldValue);
        }

        html += createRow('old', name, oldValue);
      }
      if (node.newType) {
        var newValue = node.newType == 'simple' ? escapeValue(node.newValue) : createTable(node.newValue);

        html += createRow('new', name, newValue);
      }

      return html;
    }

    function createRows(nodes) {
      var html = '';

      for (var i = 0; i < nodes.length; ++i) {
        html += createNode(nodes[i]);
      }

      return html;
    }

    function createTable(nodes) {
      var html = '' + 
          '<table>' +
          '  <tbody>' +
               createRows(nodes) +
          '  </tbody>' +
          '</table>';

      return html;
    }

    function createReport(difference) {
      var html = '' +
          '<table>' +
          '  <tbody>' +
               createNode(difference) +
          '  </tbody>' +
          '</table>';

      return html;
    }
    
    function showDifference(difference) {
      document.body.innerHTML = createReport(difference);
    }

  </script>
  <body>
    <script>

      function compareJsons(a, b) {
        var aObject = JSON.parse(a);
        var bObject = JSON.parse(b);

        var difference = compareEntries(aObject,  bObject);

        showDifference(difference);
      }

      var json1 = '{"name":"John","items":[1,2,"3",{"name":"Item 1","z":[4]},5]}';
      var json2 = '{"name":"John","items":["1",2,3,{"name":"Item 2","x":3},"a","b"]}';

      compareJsons(json1, json2);

    </script>
  </body>
</html>

 

See also

  1. JavaScript - write own compare JSONs function

  2. JavaScript - display object as expandable tree

Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.

JavaScript - JSON

Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join