python-json-patch¶
python-json-patch is a Python library for applying JSON patches (RFC 6902). Python 2.7 and 3.4+ are supported. Tests are run on both CPython and PyPy.
Contents
Tutorial¶
Please refer to RFC 6902 for the exact patch syntax.
Creating a Patch¶
Patches can be created in two ways. One way is to explicitly create a
JsonPatch
object from a list of operations. For convenience, the method
JsonPatch.from_string()
accepts a string, parses it and constructs the
patch object from it.
>>> import jsonpatch
>>> patch = jsonpatch.JsonPatch([
{'op': 'add', 'path': '/foo', 'value': 'bar'},
{'op': 'add', 'path': '/baz', 'value': [1, 2, 3]},
{'op': 'remove', 'path': '/baz/1'},
{'op': 'test', 'path': '/baz', 'value': [1, 3]},
{'op': 'replace', 'path': '/baz/0', 'value': 42},
{'op': 'remove', 'path': '/baz/1'},
])
# or equivalently
>>> patch = jsonpatch.JsonPatch.from_string('[{"op": "add", ....}]')
Another way is to diff two objects.
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
>>> patch = jsonpatch.JsonPatch.from_diff(src, dst)
# or equivalently
>>> patch = jsonpatch.make_patch(src, dst)
Applying a Patch¶
A patch is always applied to an object.
>>> doc = {}
>>> result = patch.apply(doc)
{'foo': 'bar', 'baz': [42]}
The apply
method returns a new object as a result. If in_place=True
the
object is modified in place.
If a patch is only used once, it is not necessary to create a patch object explicitly.
>>> obj = {'foo': 'bar'}
# from a patch string
>>> patch = '[{"op": "add", "path": "/baz", "value": "qux"}]'
>>> res = jsonpatch.apply_patch(obj, patch)
# or from a list
>>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}]
>>> res = jsonpatch.apply_patch(obj, patch)
Dealing with Custom Types¶
Custom JSON dump and load functions can be used to support custom types such as
decimal.Decimal. The following examples shows how the
simplejson package, which has native
support for Python’s Decimal
type, can be used to create a custom
JsonPatch
subclass with Decimal
support:
>>> import decimal
>>> import simplejson
>>> class DecimalJsonPatch(jsonpatch.JsonPatch):
@staticmethod
def json_dumper(obj):
return simplejson.dumps(obj)
@staticmethod
def json_loader(obj):
return simplejson.loads(obj, use_decimal=True,
object_pairs_hook=jsonpatch.multidict)
>>> src = {}
>>> dst = {'bar': decimal.Decimal('1.10')}
>>> patch = DecimalJsonPatch.from_diff(src, dst)
>>> doc = {'foo': 1}
>>> result = patch.apply(doc)
{'foo': 1, 'bar': Decimal('1.10')}
Instead of subclassing it is also possible to pass a dump function to
from_diff
:
>>> patch = jsonpatch.JsonPatch.from_diff(src, dst, dumps=simplejson.dumps)
a dumps function to to_string
:
>>> serialized_patch = patch.to_string(dumps=simplejson.dumps)
'[{"op": "add", "path": "/bar", "value": 1.10}]'
and load function to from_string
:
>>> import functools
>>> loads = functools.partial(simplejson.loads, use_decimal=True,
object_pairs_hook=jsonpatch.multidict)
>>> patch.from_string(serialized_patch, loads=loads)
>>> doc = {'foo': 1}
>>> result = patch.apply(doc)
{'foo': 1, 'bar': Decimal('1.10')}
The jsonpatch
module¶
Apply JSON-Patches (RFC 6902)
-
class
jsonpatch.
AddOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Adds an object property or an array element.
-
class
jsonpatch.
CopyOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Copies an object property or an array element to a new location
-
exception
jsonpatch.
InvalidJsonPatch
¶ Raised if an invalid JSON Patch is created
-
exception
jsonpatch.
JsonPatchConflict
¶ Raised if patch could not be applied due to conflict situation such as: - attempt to add object key when it already exists; - attempt to operate with nonexistence object key; - attempt to insert value to array at position beyond its size; - etc.
-
exception
jsonpatch.
JsonPatchException
¶ Base Json Patch exception
-
exception
jsonpatch.
JsonPatchTestFailed
¶ A Test operation failed
-
class
jsonpatch.
MoveOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Moves an object property or an array element to a new location.
-
class
jsonpatch.
PatchOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ A single operation inside a JSON Patch.
-
apply
(obj)¶ Abstract method that applies a patch operation to the specified object.
-
-
class
jsonpatch.
RemoveOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Removes an object property or an array element.
-
class
jsonpatch.
ReplaceOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Replaces an object property or an array element by a new value.
-
class
jsonpatch.
TestOperation
(operation, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Test value by specified location.
-
jsonpatch.
apply_patch
(doc, patch, in_place=False, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Apply list of patches to specified json document.
Parameters: - doc (dict) – Document object.
- patch (list or str) – JSON patch as list of dicts or raw JSON-encoded string.
- in_place (bool) – While
True
patch will modify target document. By default patch will be applied to document copy. - pointer_cls (Type[JsonPointer]) – JSON pointer class to use.
Returns: Patched document object.
Return type: dict
>>> doc = {'foo': 'bar'} >>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}] >>> other = apply_patch(doc, patch) >>> doc is not other True >>> other == {'foo': 'bar', 'baz': 'qux'} True >>> patch = [{'op': 'add', 'path': '/baz', 'value': 'qux'}] >>> apply_patch(doc, patch, in_place=True) == {'foo': 'bar', 'baz': 'qux'} True >>> doc == other True
-
jsonpatch.
make_patch
(src, dst, pointer_cls=<class 'jsonpointer.JsonPointer'>)¶ Generates patch by comparing two document objects. Actually is a proxy to
JsonPatch.from_diff()
method.Parameters: - src (dict) – Data source document object.
- dst (dict) – Data source document object.
- pointer_cls (Type[JsonPointer]) – JSON pointer class to use.
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]} >>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]} >>> patch = make_patch(src, dst) >>> new = patch.apply(src) >>> new == dst True
-
jsonpatch.
multidict
(ordered_pairs)¶ Convert duplicate keys values to lists.
Commandline Utilities¶
The JSON patch package contains the commandline utilities jsondiff
and
jsonpatch
.
jsondiff
¶
The program jsondiff
can be used to create a JSON patch by comparing two
JSON files
usage: jsondiff [-h] [--indent INDENT] [-u] [-v] FILE1 FILE2
Diff two JSON files
positional arguments:
FILE1
FILE2
optional arguments:
-h, --help show this help message and exit
--indent INDENT Indent output by n spaces
-u, --preserve-unicode Output Unicode character as-is without using Code Point
-v, --version show program's version number and exit
Example¶
# inspect JSON files
$ cat a.json
{ "a": [1, 2], "b": 0 }
$ cat b.json
{ "a": [1, 2, 3], "c": 100 }
# show patch in "dense" representation
$ jsondiff a.json b.json
[{"path": "/a/2", "value": 3, "op": "add"}, {"path": "/b", "op": "remove"}, {"path": "/c", "value": 100, "op": "add"}]
# show patch with some indentation
$ jsondiff a.json b.json --indent=2
[
{
"path": "/a/2",
"value": 3,
"op": "add"
},
{
"path": "/b",
"op": "remove"
},
{
"path": "/c",
"value": 100,
"op": "add"
}
]
jsonpatch
¶
The program jsonpatch
is used to apply JSON patches on JSON files.
usage: jsonpatch [-h] [--indent INDENT] [-v] ORIGINAL PATCH
Apply a JSON patch on a JSON files
positional arguments:
ORIGINAL Original file
PATCH Patch file
optional arguments:
-h, --help show this help message and exit
--indent INDENT Indent output by n spaces
-b, --backup Back up ORIGINAL if modifying in-place
-i, --in-place Modify ORIGINAL in-place instead of to stdout
-v, --version show program's version number and exit
-u, --preserve-unicode Output Unicode character as-is without using Code Point
Example¶
# create a patch
$ jsondiff a.json b.json > patch.json
# show the result after applying a patch
$ jsonpatch a.json patch.json
{"a": [1, 2, 3], "c": 100}
$ jsonpatch a.json patch.json --indent=2
{
"a": [
1,
2,
3
],
"c": 100
}
# pipe result into new file
$ jsonpatch a.json patch.json --indent=2 > c.json
# c.json now equals b.json
$ jsondiff b.json c.json
[]