json_patch
The json_patch library provides predicates for applying JSON Patch
documents as specified by RFC 6902:
Patch documents are represented as lists of JSON operation objects using the repository native JSON term representation.
API documentation
Open the ../../apis/library_index.html#json_patch link in a web browser.
Loading
To load all entities in this library from the repository checkout, load
the loader.lgt file from this directory:
| ?- logtalk_load(loader).
Testing
To test this library predicates, load the tester.lgt file:
| ?- logtalk_load(tester).
Representation
Patch documents are JSON arrays represented as Prolog lists. Each element in the list is a JSON object representing a patch operation. The public predicate provided by this library is:
| ?- json_patch::apply(Patch, OldJSON, NewJSON).
where Patch is a list of operation objects, OldJSON is the input
JSON term, and NewJSON is the patched JSON term.
Patch document structure
Each patch operation is encoded as a JSON object using any of the JSON object representations supported by the repository JSON libraries:
Curly objects using dash pairs, e.g.
{op-add, path-'/foo', value-1}List objects using
json([...]), e.g.json([op=add, path='/foo', value=1])List objects using colon pairs, e.g.
json([':'(op, add), ':'(path, '/foo')])
The operation member names are always:
oppathfromvalue
The path and from members are JSON Pointer strings parsed using
the json_pointer library. They can be represented as:
atoms, e.g.
'/items/0'chars(List), e.g.chars(['/', i, t, e, m, s, '/', '0'])codes(List), e.g.codes([0'/, 0'i, 0't, 0'e, 0'm, 0's, 0'/, 0'0])
For example:
| ?- Patch = [{op-add, path-'/baz', value-1}],
json_patch::apply(Patch, {foo-bar}, JSON).
Patch = [{op-add,path-'/baz',value-1}]
JSON = {foo-bar, baz-1}
yes
Valid operations
The library accepts the six RFC 6902 operations:
addRequired members:op,path,valueAdds a value at the target path. When the target path is empty (''), the whole document is replaced by the given value.| ?- Patch = [{op-add, path-'/items/-', value-b}], json_patch::apply(Patch, {items-[a]}, JSON). Patch = [{op-add,path-'/items/-',value-b}] JSON = {items-[a,b]} yes
removeRequired members:op,pathRemoves the value at the target path. The target must exist. Removing the root document is not supported, so an empty path fails.| ?- Patch = [{op-remove, path-'/foo'}], json_patch::apply(Patch, {foo-bar}, JSON). Patch = [{op-remove,path-'/foo'}] JSON = {} yes
replaceRequired members:op,path,valueReplaces the existing value at the target path. When the target path is empty, the whole document is replaced.| ?- Patch = [{op-replace, path-'', value-[1,2]}], json_patch::apply(Patch, {foo-bar}, JSON). Patch = [{op-replace,path-'',value-[1,2]}] JSON = [1,2] yes
moveRequired members:op,from,pathReads the value identified byfrom, removes it from the source location, and then adds it atpath.| ?- Patch = [{op-move, from-'/foo', path-'/baz'}], json_patch::apply(Patch, {foo-bar}, JSON). Patch = [{op-move,from-'/foo',path-'/baz'}] JSON = {baz-bar} yes
copyRequired members:op,from,pathReads the value identified byfromand adds a copy of it atpath.| ?- Patch = [{op-copy, from-'/foo', path-'/baz'}], json_patch::apply(Patch, {foo-bar}, JSON). Patch = [{op-copy,from-'/foo',path-'/baz'}] JSON = {foo-bar,baz-bar} yes
testRequired members:op,path,valueSucceeds only if the value at the target path is structurally equal to the givenvalue. On success,NewJSONis unified with the original document.| ?- Patch = [{op-test, path-'/flag', value- @true}], json_patch::apply(Patch, {flag- @true}, JSON). Patch = [{op-test,path-'/flag',value- @true}] JSON = {flag- @true} yes
Path rules
Paths use RFC 6901 JSON Pointer syntax.
The empty path (
'') addresses the whole JSON document.Array insertion with
addsupports the special'-'token for appending to the end of an array.Array indices must be decimal integers without leading zeros, except for the single digit
0.Intermediate containers must already exist. Operations do not create missing parent objects or arrays.
Representation notes
This implementation reuses json_pointer for parsing patch paths and
preserves the object representation found in the patched JSON term:
curly objects stay curly objects
json([...])objects stayjson([...])objectskey text representation such as atoms,
chars(List), orcodes(List)is preserved when new members are added to an existing object
The implementation also accepts patch operation objects encoded using
curly or json([...]) object notation and pointer text represented as
atoms, chars(List), or codes(List).
Failure and errors
apply/3throws aninstantiation_errorwhenPatchorOldJSONis a variable.apply/3throwstype_error(list, Patch)when the patch document is not a list.apply/3throwsdomain_error(json_patch_operation, Operation)when an operation object is malformed.Operations fail when a required target value does not exist or when a
testcomparison does not succeed.
Empty patch paths are supported for root replacement, copying to the root, moving to the root, and testing the full document.