json-patch-extended
v0.1.2
Published
Starcounter-Jack's JSON-Patch with additional capabilities
Downloads
519
Readme
JSON-Patch Extended
Includes everything from Starcounter-Jack's JSON-Patch:
- apply patches
- validate a sequence of patches
- observe for changes (and generate patches when a change is detected)
- compare two objects (to obtain the difference)
Plus addtional features:
- reverse to reverse the effects of a sequence of patches
- Simplified by being TypeScript free and packaged in one flavor
Does this follow standards?
JSON-Patch (RFC6902) is a standard format that allows you to update a JSON document by sending the changes rather than the whole document.
This repo deviates just a bit from that standard, but only with additions. The additions are as follows:
- During generation of patches, all
remove
ops include a value property and allreplace
ops include a old property. In both cases, the additional property stores the old value at the path. This is an aide for the new reverse method.
Install
Install the current version (and save it as a dependency):
npm
$ npm install json-patch-extended --save
bower
$ bower install json-patch-extended --save
Adding to your project
In a web browser
Include dist/json-patch-extended.min.js
In Node.js
Call require to get the instance:
var jsonpatch = require('json-patch-extended')
Or use import in ES6:
import jsonpatch from 'json-patch-extended'
Usage
Applying patches:
var myobj = { firstName:"Albert", contactDetails: { phoneNumbers: [ ] } };
var patches = [
{op:"replace", path:"/firstName", value:"Joachim" },
{op:"add", path:"/lastName", value:"Wester" },
{op:"add", path:"/contactDetails/phoneNumbers/0", value:{ number:"555-123" } }
];
jsonpatch.apply( myobj, patches );
// myobj == { firstName:"Joachim", lastName:"Wester", contactDetails:{ phoneNumbers[ {number:"555-123"} ] } };
Generating patches:
var myobj = { firstName:"Joachim", lastName:"Wester", contactDetails: { phoneNumbers: [ { number:"555-123" }] } };
var observer = jsonpatch.observe( myobj );
myobj.firstName = "Albert";
myobj.contactDetails.phoneNumbers[0].number = "123";
myobj.contactDetails.phoneNumbers.push({number:"456"});
var patches = jsonpatch.generate(observer);
// patches == [
// { op:"replace", path="/firstName", value:"Albert", old:"Joachim"},
// { op:"replace", path="/contactDetails/phoneNumbers/0/number", value:"123", old:"555-123"},
// { op:"add", path="/contactDetails/phoneNumbers/1", value:{number:"456"}}];
Comparing two object trees:
var objA = {user: {firstName: "Albert", lastName: "Einstein"}};
var objB = {user: {firstName: "Albert", lastName: "Collins"}};
var diff = jsonpatch.compare(objA, objB));
//diff == [{op: "replace", path: "/user/lastName", value: "Collins", old: "Einstein"}]
Validating a sequence of patches:
var obj = {user: {firstName: "Albert"}};
var patches = [{op: "replace", path: "/user/firstName", value: "Albert"}, {op: "replace", path: "/user/lastName", value: "Einstein"}];
var errors = jsonpatch.validate(patches, obj);
if (errors.length == 0) {
//there are no errors!
}
else {
for (var i=0; i < errors.length; i++) {
if (!errors[i]) {
console.log("Valid patch at index", i, patches[i]);
}
else {
console.error("Invalid patch at index", i, errors[i], patches[i]);
}
}
}
Reversing patches:
var patches = [
{op:"replace", path:"/firstName", value:"Joachim", old:"Albert" },
{op:"add", path:"/lastName", value:"Wester" },
{op:"remove", path:"/contactDetails/phoneNumbers/0", value:{ number:"555-123" } }
];
jsonpatch.reverse( patches );
// myobj == [
// {op:"replace", path:"/firstName", value:"Albert", old:"Joachim" },
// {op:"remove", path:"/lastName", value:"Wester" },
// {op:"add", path:"/contactDetails/phoneNumbers/0", value:{ number:"555-123" } }
// ];
API
jsonpatch.apply (obj
Object, patches
Array, validate
Boolean) : boolean
Applies patches
array on obj
.
If the validate
parameter is set to true
, the patch is extensively validated before applying.
An invalid patch results in throwing an error (see jsonpatch.validate
for more information about the error object).
Returns an array of results - one item for each item in patches
. The type of each item depends on type of operation applied
test
- boolean result of the testremove
,replace
andmove
- original object that has been removedadd
(only when adding to an array) - index at which item has been inserted (useful when using-
alias)
jsonpatch.observe (obj
Object, callback
Function (optional)) : observer
Object
Sets up an deep observer on obj
that listens for changes in object tree. When changes are detected, the optional
callback is called with the generated patches array as the parameter.
Returns observer
.
jsonpatch.generate (obj
Object, observer
Object) : patches
Array
If there are pending changes in obj
, returns them synchronously. If a callback
was defined in observe
method, it will be triggered synchronously as well.
If there are no pending changes in obj
, returns an empty array (length 0).
jsonpatch.unobserve (obj
Object, observer
Object) : void
Destroys the observer set up on obj
.
Any remaining changes are delivered synchronously (as in jsonpatch.generate
). Note: this is different that ES6/7 Object.unobserve
, which delivers remaining changes asynchronously.
jsonpatch.compare (obj1
Object, obj2
Object) : patches
Array
Compares object trees obj1
and obj2
and returns the difference relative to obj1
as a patches array.
If there are no differences, returns an empty array (length 0).
jsonpatch.reverse (patches
Array) : patches
Array
Reverses a patches
array. (Pass result to jsonpatch.apply to apply to an object).
Patches are reversed according to the following:
add
- becomesremove
remove
- becomesadd
replace
- old and value are switchedcopy
,move
, andtest
- ignored
jsonpatch.validate (patches
Array, tree
Object (optional)) : error
JsonPatchError
Validates a sequence of operations. If tree
parameter is provided, the sequence is additionally validated against the object tree.
If there are no errors, returns undefined. If there is an errors, returns a JsonPatchError object with the following properties:
name
String - short error codemessage
String - long human readable error messageindex
Number - index of the operation in the sequenceoperation
Object - reference to the operationtree
Object - reference to the tree
Possible errors:
Error name | Error message
------------------------------|------------
SEQUENCE_NOT_AN_ARRAY | Patch sequence must be an array
OPERATION_NOT_AN_OBJECT | Operation is not an object
OPERATION_OP_INVALID | Operation op
property is not one of operations defined in RFC-6902
OPERATION_PATH_INVALID | Operation path
property is not a valid string
OPERATION_FROM_REQUIRED | Operation from
property is not present (applicable in move
and copy
operations)
OPERATION_VALUE_REQUIRED | Operation value
property is not present, or undefined
(applicable in add
, replace
and test
operations)
OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED | Operation value
property object has at least one undefined
value (applicable in add
, replace
and test
operations)
OPERATION_PATH_CANNOT_ADD | Cannot perform an add
operation at the desired path
OPERATION_PATH_UNRESOLVABLE | Cannot perform the operation at a path that does not exist
OPERATION_FROM_UNRESOLVABLE | Cannot perform the operation from a path that does not exist
OPERATION_PATH_ILLEGAL_ARRAY_INDEX | Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index
OPERATION_VALUE_OUT_OF_BOUNDS | The specified index MUST NOT be greater than the number of elements in the array
undefined
s (JS to JSON projection)
As undefined
type does not exist in JSON, it's also not a valid value of JSON Patch operation. Therefore jsonpatch
will not generate JSON Patches that sets anything to undefined
.
Whenever a value is set to undefined
in JS, JSON-Patch methods generate
and compare
will treat it similarly to how JavaScript method JSON.stringify
(MDN) treats them:
If
undefined
(...) is encountered during conversion it is either omitted (when it is found in an object) or censored tonull
(when it is found in an array).
See the ECMAScript spec for details.
License
MIT