The JSON-delta API¶
This document is intended to describe the behaviour of the main entry points for every implementation of JSON-delta. For now, it effectively documents the top-level namespace of the Python implementation, as that is the most fully-developed implementation in the suite.
Core functions¶
-
json_delta.
diff
(left_struc, right_struc, minimal=None, verbose=True, key=None, array_align=True, compare_lengths=True, common_key_threshold=0.0)¶ Compose a sequence of diff stanzas sufficient to convert the structure
left_struc
into the structureright_struc
. (The goal is to add ‘necessary and’ to ‘sufficient’ above!).- Optional parameters:
verbose
: Print compression statistics to stderr, and warn if the setting ofminimal
contradicts the other parms.array_align
: Use_diff.needle_diff()
to compute deltas between arrays. Relatively computationally expensive, but likely to produce shorter diffs. Defaults toTrue
.compare_lengths
: If[[key, right_struc]]
can be encoded as a shorter JSON-string, return it instead of examining the internal structure ofleft_struc
andright_struc
. It involves callingjson.dumps()
twice for every node in the structure, but may result in smaller diffs. Defaults toTrue
.common_key_threshold
: Skip recursion intoleft_struc
andright_struc
if the fraction of keys they have in common (with the same value) is less than this parm (which should be a float between0.0
and1.0
). Defaults to 0.0.minimal
: Included for backwards compatibility.True
is equivalent to(array_align=True, compare_lengths=True, common_key_threshold=0.0)
;False
is equivalent to(array_align=False, compare_lengths=False, common_key_threshold=0.5)
. Specific settings ofarray_align
,compare_lengths
orcommon_key_threshold
will supersede this parm, warning on stderr ifverbose
andminimal
are both set.key
: Also included for backwards compatibility. If set, will be prepended to the key in each stanza of the output.
The parameter
key
is present because this function is mutually recursive with_diff.needle_diff()
and_diff.keyset_diff()
. If set to a list, it will be prefixed to every keypath in the output.
-
json_delta.
patch
(struc, diff, in_place=True)¶ Apply the sequence of diff stanzas
diff
to the structurestruc
.By default, this function modifies
struc
in place; setin_place
toFalse
to return a patched copy of struc instead:>>> will_change = [16] >>> wont_change = [16] >>> patch(will_change, [[[0]]]) [] >>> will_change [] >>> patch(wont_change, [[[0]]], False) [] >>> wont_change [16]
-
json_delta.
udiff
(left, right, patch=None, indent=0, use_ellipses=True, entry=True)¶ Render the difference between the structures
left
andright
as a string in a fashion inspired by diff -u.Generating a udiff is strictly slower than generating a normal diff with the same option parameters, since the udiff is computed on the basis of a normal diff between
left
andright
. If such a diff has already been computed (e.g. by callingdiff()
), pass it as thepatch
parameter:>>> (next(udiff({"foo": None}, {"foo": None}, patch=[])) == ... ' {...}') True
As you can see above, structures that are identical in
left
andright
are abbreviated using'...'
by default. To disable this behavior, setuse_ellipses
toFalse
.>>> ('\n'.join(udiff({"foo": None}, {"foo": None}, ... patch=[], use_ellipses=False)) == ... """ { ... "foo": ... null ... }""") True
>>> ('\n'.join(udiff([None, None, None], [None, None, None], ... patch=[], use_ellipses=False)) == ... """ [ ... null, ... null, ... null ... ]""") True
-
json_delta.
upatch
(struc, udiff, reverse=False, in_place=True)¶ Apply a patch as output by
json_delta.udiff()
tostruc
.As with
json_delta.patch()
,struc
is modified in place by default. Set the parmin_place
toFalse
if this is not the desired behaviour.The udiff format has enough information in it that this transformation can be applied in reverse: i.e. if
udiff
is the output ofudiff(left, right)
, you can reconstructright
givenleft
andudiff
(by runningupatch(left, udiff)
), or you can also reconstructleft
givenright
and udiff (by runningupatch(right, udiff, reverse=True)
). This is not possible for JSON-format diffs, since a[keypath]
stanza (meaning “delete the structure atkeypath
”) does not record what the deleted structure was.
load_and_*¶
For convenience when handling input that is already JSON-serialized, implementations should offer entry points named load_and_{FUNC}, which deserialize their input and then apply {FUNC} to it.
-
json_delta.
load_and_diff
(left=None, right=None, both=None, array_align=None, compare_lengths=None, common_key_threshold=None, minimal=None, verbose=True)¶ Apply
diff()
to strings or files representing JSON-serialized structures.Specify either
left
andright
, orboth
, like so:>>> (load_and_diff('{"foo":"bar"}', '{"foo":"baz"}', verbose=False) ... == [[["foo"],"baz"]]) True >>> (load_and_diff(both='[{"foo":"bar"},{"foo":"baz"}]', verbose=False) ... == [[["foo"],"baz"]]) True
left
,right
andboth
may be either strings (instances of basestring in 2.7) or file-like objects.minimal
andverbose
are passed through todiff()
, which see.A call to this function with string arguments is strictly equivalent to calling
diff(json.loads(left), json.loads(right), minimal=minimal, verbose=verbose)
ordiff(*json.loads(both), minimal=minimal, verbose=verbose)
, as appropriate.
-
json_delta.
load_and_patch
(struc=None, stanzas=None, both=None)¶ Apply
patch()
to strings or files representing JSON-serialized structures.Specify either
struc
andstanzas
, orboth
, like so:>>> (load_and_patch('{"foo":"bar"}', '[[["foo"],"baz"]]') == ... {"foo": "baz"}) True >>> (load_and_patch(both='[{"foo":"bar"},[[["foo"],"baz"]]]') == ... {"foo": "baz"}) True
struc
,stanzas
andboth
may be either strings (instances of basestring in 2.7) or file-like objects.A call to this function with string arguments is strictly equivalent to calling
patch(json.loads(struc), json.loads(stanzas), in_place=in_place)
orpatch(*json.loads(both), in_place=in_place)
, as appropriate.
-
json_delta.
load_and_udiff
(left=None, right=None, both=None, stanzas=None, indent=0)¶ Apply
udiff()
to strings representing JSON-serialized structures.Specify either
left
andright
, orboth
, like so:>>> udiff = """ { ... "foo": ... - "bar" ... + "baz" ... }""" >>> test = load_and_udiff('{"foo":"bar"}', '{"foo":"baz"}') >>> '\n'.join(test) == udiff True >>> test = load_and_udiff(both='[{"foo":"bar"},{"foo":"baz"}]') >>> '\n'.join(test) == udiff True
left
,right
andboth
may be either strings (instances of basestring in 2.7) or file-like objects.stanzas
andindent
are passed through toudiff()
, which see.A call to this function with string arguments is strictly equivalent to calling
udiff(json.loads(left), json.loads(right), stanzas=stanzas, indent=indent)
orudiff(*json.loads(both), stanzas=stanzas, indent=indent)
, as appropriate.
-
json_delta.
load_and_upatch
(struc=None, json_udiff=None, both=None, reverse=False)¶ Apply
upatch()
to strings representing JSON-serialized structures.Specify either
struc
andjson_udiff
, orboth
, like so:>>> struc = '{"foo":"bar"}' >>> json_udiff = r'" {\n \"foo\":\n- \"bar\"\n+ \"baz\"\n }"' >>> both = r'[{"foo":"baz"}," '\ ... r'{\n \"foo\":\n- \"bar\"\n+ \"baz\"\n }"]' >>> load_and_upatch(struc, json_udiff) == {"foo": "baz"} True >>> load_and_upatch(both=both, reverse=True) == {"foo": "bar"} True
struc
,json_udiff
andboth
may be either strings (instances of basestring in 2.7) or file-like objects. Note thatjson_udiff
is so named because it must be a JSON-serialized representation of the udiff string, not the udiff string itself.reverse
is passed through toupatch()
, which see.A call to this function with string arguments is strictly equivalent to calling
upatch(json.loads(struc), json.loads(json_udiff), reverse=reverse, in_place=in_place)
orupatch(*json.loads(both), reverse=reverse, in_place=in_place)
, as appropriate.