generate-json-patch
Create RFC 6902 compliant JSON Patch objects based on two given JSON objects with a configurable interface.
TL;DR
- Can diff any two JSON compliant objects - returns differences as JSON Patch.
- Elegant array diffing by providing an
objectHash
to match array elements - Ignore specific keys by providing a
propertyFilter
-
move
operations are ALWAYS appended at the end, therefore, they can be ignored (if wanted) when the patch gets applied. - 🐾 Is it small? Zero dependencies - it's ~7 KB (uncompressed).
- 🔮 Is it fast? I haven't done any performance comparison yet.
- 🐥 Is it stable? Test coverage is high, but it's still in its early days - bugs are expected.
- The interface is inspired by jsondiffpatch
- 100% Typescript
Installation
Works on node and browser environments.
npm install generate-json-patch
Usage
import { generateJSONPatch } from 'generate-json-patch';
const before = { manufacturer: "Ford", type: "Granada", year: 1972 };
const after = { manufacturer: "Ford", type: "Granada", year: 1974 };
const patch = generateJSONPatch(before, after);
console.log(patch) // => [{op: 'replace', path: '/year', value: 1974}]
Configuration
import { generateJSONPatch, JsonPatchConfig, JsonValue } from 'generate-json-patch';
generateJSONPatch({/*...*/}, {/*...*/}, {
// called when comparing array elements
objectHash: function(value: JsonValue, context: GeneratePatchContext) {
// for arrays of primitive values like string and numbers, a stringification is sufficent:
// return JSON.stringify(value)
// If we know the shape of the value, we can match be specific properties
return value.name
},
// called for every property on objects. Can be used to ignore sensitive or irrelevant
// properties when comparing data.
propertyFilter: function (propertyName: string, context: GeneratePatchContext) {
return !['sensitiveProperty'].includes(propertyName);
},
array: {
// When true, no move operations will be created.
// The rersulting patch will not lead to identical objects,
// as postions of array elements can be different!
ignoreMove: true
}
});
Patch Context
Both config function (objectHash
, propertyFilter
), receive a patch context as second parameter.
This allows for granular decision-making on the provided data.
Example
import {generateJSONPatch, JsonPatchConfig, JsonValue, pathInfo} from 'generate-json-patch';
const before = {
manufacturer: "Ford",
type: "Granada",
colors: ['red', 'silver', 'yellow'],
engine: [
{ name: 'Cologne V6 2.6', hp: 125 },
{ name: 'Cologne V6 2.0', hp: 90 },
{ name: 'Cologne V6 2.3', hp: 108 },
{ name: 'Essex V6 3.0', hp: 138 },
]
}
const after = {
manufacturer: "Ford",
type: "Granada",
colors: ['red', 'silver', 'yellow'],
engine: [
{name: 'Essex V6 3.0', hp: 138},
{name: 'Cologne V6 2.6', hp: 125},
{name: 'Cologne V6 2.0', hp: 90},
{name: 'Cologne V6 2.3', hp: 108},
]
}
const patch = generateJSONPatch(before, after, {
objectHash: function (value: JsonValue, context: GeneratePatchContext) {
const {length, last} = pathInfo(context.path)
if (length === 2 && last === 'engine') {
return value.name
}
return JSON.stringify(value)
}
});
console.log(patch) // => [
// { op: 'replace', path: '/engine/3/hp', value: 138 },
// { op: 'move', from: '/engine/2', path: '/engine/3' },
// { op: 'move', from: '/engine/1', path: '/engine/2' },
// { op: 'move', from: '/engine/0', path: '/engine/1' }]
For more examples, check out the tests