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_strucinto 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 ofminimalcontradicts 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_strucandright_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_strucandright_strucif the fraction of keys they have in common (with the same value) is less than this parm (which should be a float between0.0and1.0). Defaults to 0.0.minimal: Included for backwards compatibility.Trueis equivalent to(array_align=True, compare_lengths=True, common_key_threshold=0.0);Falseis equivalent to(array_align=False, compare_lengths=False, common_key_threshold=0.5). Specific settings ofarray_align,compare_lengthsorcommon_key_thresholdwill supersede this parm, warning on stderr ifverboseandminimalare both set.key: Also included for backwards compatibility. If set, will be prepended to the key in each stanza of the output.
The parameter
keyis 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
diffto the structurestruc.By default, this function modifies
strucin place; setin_placetoFalseto 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
leftandrightas 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
leftandright. If such a diff has already been computed (e.g. by callingdiff()), pass it as thepatchparameter:>>> (next(udiff({"foo": None}, {"foo": None}, patch=[])) == ... ' {...}') True
As you can see above, structures that are identical in
leftandrightare abbreviated using'...'by default. To disable this behavior, setuse_ellipsestoFalse.>>> ('\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(),strucis modified in place by default. Set the parmin_placetoFalseif 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
udiffis the output ofudiff(left, right), you can reconstructrightgivenleftandudiff(by runningupatch(left, udiff)), or you can also reconstructleftgivenrightand 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
leftandright, 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,rightandbothmay be either strings (instances of basestring in 2.7) or file-like objects.minimalandverboseare 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
strucandstanzas, 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,stanzasandbothmay 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
leftandright, 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,rightandbothmay be either strings (instances of basestring in 2.7) or file-like objects.stanzasandindentare 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
strucandjson_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_udiffandbothmay be either strings (instances of basestring in 2.7) or file-like objects. Note thatjson_udiffis so named because it must be a JSON-serialized representation of the udiff string, not the udiff string itself.reverseis 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.