Multilevel JSON diff in python
Multilevel JSON diff in python
Check out this python library jsondiff , that will help you to identify the diffs
import json
import jsondiff
json1 = json.loads(
{isDynamic: false, name: , value: SID:<sid>, description: instance,argsOrder: 1,isMultiSelect: false})
json2 = json.loads(
{ name: , value: SID:<sid>,isDynamic: false, description: instance,argsOrder: 1,isMultiSelect: false})
res = jsondiff.diff(json1, json2)
if res:
print(Diff found)
else:
print(Same)
Yes! You can diff it with jycm which has a rendering tool out of the box.
It uses LCS, Edit distance and Kuhn–Munkres to diff arrays.
Heres an universal example with set in set and value changes in some set
from jycm.helper import make_ignore_order_func
from jycm.jycm import YouchamaJsonDiffer
left = {
set_in_set: [
{
id: 1,
label: label:1,
set: [
1,
5,
3
]
},
{
id: 2,
label: label:2,
set: [
4,
5,
6
]
}
]
}
right = {
set_in_set: [
{
id: 2,
label: label:2,
set: [
6,
5,
4
]
},
{
id: 1,
label: label:1111,
set: [
3,
2,
1
]
}
]
}
ycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([
^set_in_set$,
^set_in_set->\[\d+\]->set$
]))
ycm.diff()
expected = {
list:add: [
{left: __NON_EXIST__, right: 2, left_path: , right_path: set_in_set->[1]->set->[1]}
],
list:remove: [
{left: 5, right: __NON_EXIST__, left_path: set_in_set->[0]->set->[1], right_path: }
],
value_changes: [
{left: label:1, right: label:1111, left_path: set_in_set->[0]->label,
right_path: set_in_set->[1]->label, old: label:1, new: label:1111}
]
}
assert ycm.to_dict(no_pairs=True) == expected
you can set no_pairs=False to get the all pairs. Heres a rendered example:
As for the example here, you can use it as:
from jycm.helper import make_ignore_order_func
from jycm.jycm import YouchamaJsonDiffer
left = {
data: [{x: 1, y: 2}, {x: 3, y: 4}]
}
right = {
data: [{x: 3, y: 4}, {x: 1, y: 2}]
}
ycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([
^data,
]))
ycm.diff()
assert ycm.to_dict(no_pairs=True) == {}
Bonus, you the values are interrupted as coordinates on plain, you can even define a operator to
determine whether two points should be matched!(Then comparing their values)
Heres the code:
from typing import Tuple
from jycm.helper import make_ignore_order_func
from jycm.jycm import YouchamaJsonDiffer
from jycm.operator import BaseOperator
import math
left = {
data: [
{x: 1, y: 1},
{x: 10, y: 10},
{x: 100, y: 100}
]
}
right = {
data: [
{x: 150, y: 150},
{x: 10, y: 11},
{x: 2, y: 3}
]
}
class L2DistanceOperator(BaseOperator):
__operator_name__ = operator:l2distance
__event__ = operator:l2distance
def __init__(self, path_regex, distance_threshold):
super().__init__(path_regex=path_regex)
self.distance_threshold = distance_threshold
def diff(self, level: TreeLevel, instance, drill: bool) -> Tuple[bool, float]:
distance = math.sqrt(
(level.left[x] - level.right[x]) ** 2 + (level.left[y] - level.right[y]) ** 2
)
info = {
distance: distance,
distance_threshold: self.distance_threshold,
pass: distance < self.distance_threshold
}
if not drill:
instance.report(self.__event__, level, info)
return False, 1 if info[pass] else 0
return True, 1 if info[pass] else 0
ycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([
^data$,
]), custom_operators=[
L2DistanceOperator(^data->\[.*\]$, 10),
])
ycm.diff()
expected = {
just4vis:pairs: [
{left: 1, right: 2, left_path: data->[0]->x, right_path: data->[2]->x},
{left: {x: 1, y: 1}, right: {x: 2, y: 3}, left_path: data->[0],
right_path: data->[2]},
{left: 1, right: 3, left_path: data->[0]->y, right_path: data->[2]->y},
{left: {x: 1, y: 1}, right: {x: 2, y: 3}, left_path: data->[0],
right_path: data->[2]},
{left: {x: 1, y: 1}, right: {x: 2, y: 3}, left_path: data->[0],
right_path: data->[2]}
],
list:add: [
{left: __NON_EXIST__, right: {x: 150, y: 150}, left_path: , right_path: data->[0]}
],
list:remove: [
{left: {x: 100, y: 100}, right: __NON_EXIST__, left_path: data->[2], right_path: }
],
operator:l2distance: [
{left: {x: 1, y: 1}, right: {x: 2, y: 3}, left_path: data->[0],
right_path: data->[2], distance: 2.23606797749979, distance_threshold: 10,
pass: True},
{left: {x: 10, y: 10}, right: {x: 10, y: 11}, left_path: data->[1],
right_path: data->[1], distance: 1.0, distance_threshold: 10,
pass: True}
],
value_changes: [
{left: 1, right: 2, left_path: data->[0]->x, right_path: data->[2]->x, old: 1, new: 2},
{left: 1, right: 3, left_path: data->[0]->y, right_path: data->[2]->y, old: 1, new: 3},
{left: 10, right: 11, left_path: data->[1]->y, right_path: data->[1]->y, old: 10, new: 11}
]
}
assert ycm.to_dict() == expected
As you can see jycm report addition and remove for points {x: 150, y: 150}
and {x: 100, y: 100}
for their
distances are too far (more than 10) and value-change for the other two points.
P.S. RENDERER DEMO
Multilevel JSON diff in python
solved it partially with following function
def diff(prev,lat):
p=prev
l=lat
prevDiff = []
latDiff = []
for d1 in p[:]:
flag = False
for d2 in l:
if len(set(d1.items()) ^ set(d2.items())) == 0:
p.remove(d1)
l.remove(d2)
flag = True
break
if not flag:
prevDiff.append(d1)
p.remove(d1)
prevDiff = prevDiff + p
latDiff = latDiff + l
resJSONdata=[]
if len(prevDiff) != 0:
resJSONdata.append({prevCount:len(prevDiff)})
resJSONdata.append({prev:prevDiff})
if len(latDiff) != 0:
resJSONdata.append({latestCount:len(latDiff)})
resJSONdata.append({latest:latDiff})
# return json.dumps(resJSONdata,indent = 4,sort_keys=True)
return resJSONdata
its not doing it recursively into level into levels but for my purpose this solved the issue