python中的多级JSON diff

kew*_*iev 2 python diff json compare unordered

请链接我回答如果已经回答,我的问题是我想获得多级json的差异,这是无序的.

x=json.loads('''[{"y":2,"x":1},{"x":3,"y":4}]''')
y=json.loads('''[{"x":1,"y":2},{"x":3,"y":4}]''')
z=json.loads('''[{"x":3,"y":4},{"x":1,"y":2}]''')

import json_tools as jt
import json_delta as jd


print jt.diff(y,z)
print jd.diff(y,z)
print y==z
print x==y
Run Code Online (Sandbox Code Playgroud)

输出是

[{'prev': 2, 'value': 4, 'replace': u'/0/y'}, {'prev': 1, 'value': 3, 'replace': u'/0/x'}, {'prev': 4, 'value': 2, 'replace': u'/1/y'}, {'prev': 3, 'value': 1, 'replace': u'/1/x'}]
[[[2], {u'y': 2, u'x': 1}], [[0]]]
False
True
Run Code Online (Sandbox Code Playgroud)

我的问题是如何让y和z相等,或者如果存在实际差异,则取决于JSON的非顺序.

一种无序的词典列表,但我正在寻找一些水平证明的东西,列表/词典的列表/词典......

Jen*_*ish 12

看看这个python库jsondiff,它将帮助你识别差异

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")
Run Code Online (Sandbox Code Playgroud)


egg*_*cat 5

更新:请参阅https://eggachecat.github.io/jycm-json-diff-viewer/进行现场演示!现在它有一个 JS 原生实现。

\n

隶属关系:我是该库的作者。

\n

是的!您可以使用jycm对其进行比较, jycm具有开箱即用的渲染工具。

\n

它使用 LCS、编辑距离和 Kuhn\xe2\x80\x93Munkres 来比较数组。

\n

这是一个通用示例,其中集合中的集合和某些集合中的值更改

\n
from jycm.helper import make_ignore_order_func\nfrom jycm.jycm import YouchamaJsonDiffer\n\nleft = {\n    "set_in_set": [\n        {\n            "id": 1,\n            "label": "label:1",\n            "set": [\n                1,\n                5,\n                3\n            ]\n        },\n        {\n            "id": 2,\n            "label": "label:2",\n            "set": [\n                4,\n                5,\n                6\n            ]\n        }\n    ]\n}\n\nright = {\n    "set_in_set": [\n        {\n            "id": 2,\n            "label": "label:2",\n            "set": [\n                6,\n                5,\n                4\n            ]\n        },\n        {\n            "id": 1,\n            "label": "label:1111",\n            "set": [\n                3,\n                2,\n                1\n            ]\n        }\n    ]\n}\n\nycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([\n    "^set_in_set$",\n    "^set_in_set->\\\\[\\\\d+\\\\]->set$"\n]))\n\nycm.diff()\n\nexpected = {\n    \'list:add\': [\n        {\'left\': \'__NON_EXIST__\', \'right\': 2, \'left_path\': \'\', \'right_path\': \'set_in_set->[1]->set->[1]\'}\n    ],\n    \'list:remove\': [\n        {\'left\': 5, \'right\': \'__NON_EXIST__\', \'left_path\': \'set_in_set->[0]->set->[1]\', \'right_path\': \'\'}\n    ],\n    \'value_changes\': [\n        {\'left\': \'label:1\', \'right\': \'label:1111\', \'left_path\': \'set_in_set->[0]->label\',\n         \'right_path\': \'set_in_set->[1]->label\', \'old\': \'label:1\', \'new\': \'label:1111\'}\n    ]\n}\n\nassert ycm.to_dict(no_pairs=True) == expected\n
Run Code Online (Sandbox Code Playgroud)\n

您可以设置no_pairs=False来获取所有对。这是一个渲染的示例:

\n

在此输入图像描述

\n

至于这里的示例,您可以将其用作:

\n
from jycm.helper import make_ignore_order_func\nfrom jycm.jycm import YouchamaJsonDiffer\n\nleft = {\n    "data": [{"x": 1, "y": 2}, {"x": 3, "y": 4}]\n}\n\nright = {\n    "data": [{"x": 3, "y": 4}, {"x": 1, "y": 2}]\n}\n\nycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([\n    "^data",\n]))\n\nycm.diff()\n\nassert ycm.to_dict(no_pairs=True) == {}\n
Run Code Online (Sandbox Code Playgroud)\n

另外,您的值会被中断为平面上的坐标,您甚至可以定义一个运算符来\n确定两个点是否应该匹配!(然后比较它们的值)

\n

这是代码:

\n
from typing import Tuple\n\nfrom jycm.helper import make_ignore_order_func\nfrom jycm.jycm import YouchamaJsonDiffer\nfrom jycm.operator import BaseOperator\nimport math\n\nleft = {\n    "data": [\n        {"x": 1, "y": 1},\n        {"x": 10, "y": 10},\n        {"x": 100, "y": 100}\n    ]\n}\n\nright = {\n    "data": [\n        {"x": 150, "y": 150},\n        {"x": 10, "y": 11},\n        {"x": 2, "y": 3}\n    ]\n}\n\n\nclass L2DistanceOperator(BaseOperator):\n    __operator_name__ = "operator:l2distance"\n    __event__ = "operator:l2distance"\n\n    def __init__(self, path_regex, distance_threshold):\n        super().__init__(path_regex=path_regex)\n        self.distance_threshold = distance_threshold\n\n    def diff(self, level: \'TreeLevel\', instance, drill: bool) -> Tuple[bool, float]:\n        distance = math.sqrt(\n            (level.left["x"] - level.right["x"]) ** 2 + (level.left["y"] - level.right["y"]) ** 2\n        )\n        info = {\n            "distance": distance,\n            "distance_threshold": self.distance_threshold,\n            "pass": distance < self.distance_threshold\n        }\n\n        if not drill:\n            instance.report(self.__event__, level, info)\n            return False, 1 if info["pass"] else 0\n        return True, 1 if info["pass"] else 0\n\n\nycm = YouchamaJsonDiffer(left, right, ignore_order_func=make_ignore_order_func([\n    "^data$",\n]), custom_operators=[\n    L2DistanceOperator("^data->\\\\[.*\\\\]$", 10),\n])\n\nycm.diff()\n\nexpected = {\n    \'just4vis:pairs\': [\n        {\'left\': 1, \'right\': 2, \'left_path\': \'data->[0]->x\', \'right_path\': \'data->[2]->x\'},\n        {\'left\': {\'x\': 1, \'y\': 1}, \'right\': {\'x\': 2, \'y\': 3}, \'left_path\': \'data->[0]\',\n         \'right_path\': \'data->[2]\'},\n        {\'left\': 1, \'right\': 3, \'left_path\': \'data->[0]->y\', \'right_path\': \'data->[2]->y\'},\n        {\'left\': {\'x\': 1, \'y\': 1}, \'right\': {\'x\': 2, \'y\': 3}, \'left_path\': \'data->[0]\',\n         \'right_path\': \'data->[2]\'},\n        {\'left\': {\'x\': 1, \'y\': 1}, \'right\': {\'x\': 2, \'y\': 3}, \'left_path\': \'data->[0]\',\n         \'right_path\': \'data->[2]\'}\n    ],\n    \'list:add\': [\n        {\'left\': \'__NON_EXIST__\', \'right\': {\'x\': 150, \'y\': 150}, \'left_path\': \'\', \'right_path\': \'data->[0]\'}\n    ],\n    \'list:remove\': [\n        {\'left\': {\'x\': 100, \'y\': 100}, \'right\': \'__NON_EXIST__\', \'left_path\': \'data->[2]\', \'right_path\': \'\'}\n    ],\n    \'operator:l2distance\': [\n        {\'left\': {\'x\': 1, \'y\': 1}, \'right\': {\'x\': 2, \'y\': 3}, \'left_path\': \'data->[0]\',\n         \'right_path\': \'data->[2]\', \'distance\': 2.23606797749979, \'distance_threshold\': 10,\n         \'pass\': True},\n        {\'left\': {\'x\': 10, \'y\': 10}, \'right\': {\'x\': 10, \'y\': 11}, \'left_path\': \'data->[1]\',\n         \'right_path\': \'data->[1]\', \'distance\': 1.0, \'distance_threshold\': 10,\n         \'pass\': True}\n    ],\n    \'value_changes\': [\n        {\'left\': 1, \'right\': 2, \'left_path\': \'data->[0]->x\', \'right_path\': \'data->[2]->x\', \'old\': 1, \'new\': 2},\n        {\'left\': 1, \'right\': 3, \'left_path\': \'data->[0]->y\', \'right_path\': \'data->[2]->y\', \'old\': 1, \'new\': 3},\n        {\'left\': 10, \'right\': 11, \'left_path\': \'data->[1]->y\', \'right_path\': \'data->[1]->y\', \'old\': 10, \'new\': 11}\n    ]\n}\nassert ycm.to_dict() == expected\n
Run Code Online (Sandbox Code Playgroud)\n

正如您所看到的,jycm 报告了点的添加和删除{\'x\': 150, \'y\': 150}以及{\'x\': 100, \'y\': 100}它们\n距离太远(超过 10)以及其他两个点的值更改。

\n

PS 渲染器演示

\n

gif 展示

\n


kew*_*iev -5

用以下函数部分解决了这个问题

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
Run Code Online (Sandbox Code Playgroud)

它不是递归地进入级别,但出于我的目的,这解决了问题