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:

enter

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

gif-show

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

Leave a Reply

Your email address will not be published. Required fields are marked *