Stars: 193
Forks: 28
Pull Requests: 42
Issues: 23
Watchers: 7
Last Updated: 2023-07-07 15:45:34
JSON diff/rearrange/patch/pointer library for PHP
License: MIT License
Languages: Makefile, PHP
A PHP implementation for finding unordered diff between two JSON documents.
JSON files you can use a standard diff tool on rearranged pretty-printed JSON.JSON.swagger.json parameters list).git clone https://github.com/swaggest/json-diff.gitcomposer require swaggest/json-diffJsonDiffCreate JsonDiff object from two values (original and new).
$r = new JsonDiff(json_decode($originalJson), json_decode($newJson));On construction JsonDiff will build rearranged value of new recursively keeping original keys order where possible.
Keys that are missing in original will be appended to the end of rearranged value in same order they had in new value.
If two values are arrays of objects, JsonDiff will try to find a common unique field in those objects and use it as criteria for rearranging.
You can enable this behaviour with JsonDiff::REARRANGE_ARRAYS option:
$r = new JsonDiff(
json_decode($originalJson),
json_decode($newJson),
JsonDiff::REARRANGE_ARRAYS
);Available options:
REARRANGE_ARRAYS is an option to enable arrays rearrangement to minimize the difference.STOP_ON_DIFF is an option to improve performance by stopping comparison when a difference is found.JSON_URI_FRAGMENT_ID is an option to use URI Fragment Identifier Representation (example: "#/c%25d"). If not set default JSON String Representation (example: "/c%d").SKIP_JSON_PATCH is an option to improve performance by not building JsonPatch for this diff.SKIP_JSON_MERGE_PATCH is an option to improve performance by not building JSON Merge Patch value for this diff.TOLERATE_ASSOCIATIVE_ARRAYS is an option to allow associative arrays to mimic JSON objects (not recommended).COLLECT_MODIFIED_DIFF is an option to enable getModifiedDiff.Options can be combined, e.g. JsonDiff::REARRANGE_ARRAYS + JsonDiff::STOP_ON_DIFF.
getDiffCntReturns total number of differences
getPatchReturns JsonPatch of difference
getMergePatchReturns JSON Merge Patch value of difference
getRearrangedReturns new value, rearranged with original order.
getRemovedReturns removals as partial value of original.
getRemovedPathsReturns list of JSON paths that were removed from original.
getRemovedCntReturns number of removals.
getAddedReturns additions as partial value of new.
getAddedPathsReturns list of JSON paths that were added to new.
getAddedCntReturns number of additions.
getModifiedOriginalReturns modifications as partial value of original.
getModifiedNewReturns modifications as partial value of new.
getModifiedDiffReturns list of ModifiedPathDiff containing paths with original and new values.
Not collected by default, requires JsonDiff::COLLECT_MODIFIED_DIFF option.
getModifiedPathsReturns list of JSON paths that were modified from original to new.
getModifiedCntReturns number of modifications.
JsonPatchimportCreates JsonPatch instance from JSON-decoded data.
exportCreates patch data from JsonPatch object.
opAdds operation to JsonPatch.
applyApplies patch to JSON-decoded data.
setFlagsAlters default behavior.
Available flags:
JsonPatch::STRICT_MODE Disallow converting empty array to object for key creation.JsonPatch::TOLERATE_ASSOCIATIVE_ARRAYS Allow associative arrays to mimic JSON objects (not recommended).JsonPointerescapeSegmentEscapes path segment.
splitPathCreates array of unescaped segments from JSON Pointer string.
buildPathCreates JSON Pointer string from array of unescaped segments.
addAdds value to data at path specified by segments.
getGets value from data at path specified by segments.
getByPointerGets value from data at path specified JSON Pointer string.
removeRemoves value from data at path specified by segments.
JsonMergePatchapplyApplies patch to JSON-decoded data.
JsonValueReplaceprocessRecursively replaces all nodes equal to search value with replace value.
$originalJson = <<<'JSON'
{
"key1": [4, 1, 2, 3],
"key2": 2,
"key3": {
"sub0": 0,
"sub1": "a",
"sub2": "b"
},
"key4": [
{"a":1, "b":true, "subs": [{"s":1}, {"s":2}, {"s":3}]}, {"a":2, "b":false}, {"a":3}
]
}
JSON;
$newJson = <<<'JSON'
{
"key5": "wat",
"key1": [5, 1, 2, 3],
"key4": [
{"c":false, "a":2}, {"a":1, "b":true, "subs": [{"s":3, "add": true}, {"s":2}, {"s":1}]}, {"c":1, "a":3}
],
"key3": {
"sub3": 0,
"sub2": false,
"sub1": "c"
}
}
JSON;
$patchJson = <<<'JSON'
[
{"value":4,"op":"test","path":"/key1/0"},
{"value":5,"op":"replace","path":"/key1/0"},
{"op":"remove","path":"/key2"},
{"op":"remove","path":"/key3/sub0"},
{"value":"a","op":"test","path":"/key3/sub1"},
{"value":"c","op":"replace","path":"/key3/sub1"},
{"value":"b","op":"test","path":"/key3/sub2"},
{"value":false,"op":"replace","path":"/key3/sub2"},
{"value":0,"op":"add","path":"/key3/sub3"},
{"value":true,"op":"add","path":"/key4/0/subs/2/add"},
{"op":"remove","path":"/key4/1/b"},
{"value":false,"op":"add","path":"/key4/1/c"},
{"value":1,"op":"add","path":"/key4/2/c"},
{"value":"wat","op":"add","path":"/key5"}
]
JSON;
$diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), JsonDiff::REARRANGE_ARRAYS);
$this->assertEquals(json_decode($patchJson), $diff->getPatch()->jsonSerialize());
$original = json_decode($originalJson);
$patch = JsonPatch::import(json_decode($patchJson));
$patch->apply($original);
$this->assertEquals($diff->getRearranged(), $original);Due to magical methods and other restrictions PHP classes can not be reliably mapped to/from JSON objects.
There is support for objects of PHP classes in JsonPointer with limitations:
null is equal to non-existentWhen JsonDiff::REARRANGE_ARRAYS option is enabled, array items are ordered to match the original array.
If arrays contain homogenous objects, and those objects have a common property with unique values, array is ordered to match placement of items with same value of such property in the original array.
Example: original
[{"name": "Alex", "height": 180},{"name": "Joe", "height": 179},{"name": "Jane", "height": 165}]vs new
[{"name": "Joe", "height": 179},{"name": "Jane", "height": 168},{"name": "Alex", "height": 180}]would produce a patch:
[{"value":165,"op":"test","path":"/2/height"},{"value":168,"op":"replace","path":"/2/height"}]If qualifying indexing property is not found, rearrangement is done based on items equality.
Example: original
{"data": [{"A": 1, "C": [1, 2, 3]}, {"B": 2}]}vs new
{"data": [{"B": 2}, {"A": 1, "C": [3, 2, 1]}]}would produce no difference.
Moved to swaggest/json-cli