EN
JavaScript - custom compare JSONs function
8
points
In this article, we're going to have a look at how to write own function to compare JSONs. Approach used in this article encloses each entity with object that keeps previous and current values with types and kind of change.
Example result:
{
"name": "propertyName",
"status": "changed",
"oldType": "simple",
"newType": "simple",
"oldValue": "Some old value",
"newValue": "Some new value"
}
Where:
status
can be one of:unchanged
,updated
,created
ordeleted
,- old and new type:
array
,object
,simple
ornull
, - old and new value: simple type object, array with enclosed entities that describe nested objects or
null
.
Note: read this article to see json difference visualization.
Run below example to see details.
Reusable example
This section presents simple logic that returns object with difference information.
// ONLINE-RUNNER:browser;
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,
};
}
function createDeletedItem(key, type, value) {
return createOldItem(key, 'deleted', type, value);
}
function createCreatedItem(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)),
};
}
}
function compareJsons(a, b) {
var aEntity = JSON.parse(a);
var bEntity = JSON.parse(b);
return compareEntries(aEntity, bEntity);
}
// Usage example:
var json1 = '{"name":"John","items":[1,2,"3",{"name":"Item","z":[4]},5]}';
var json2 = '{"name":"John","items":["1",2,3,{"name":"Item 2","x":3}]}';
var difference = compareJsons(json1, json2);
console.log(JSON.stringify(difference, null, 4));