python 中的多级 JSON 差异
Multilevel JSON diff in python
请link我回答如果已经回答了,
我的问题是我想获得无序的多级差异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
输出是
[{'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
我的问题是如何让 y 和 z 相等,或者是否存在取决于 JSON 的非顺序的实际差异。
一种无序的字典列表,但我正在寻找 list/dict 的 list/dictionaries 字典的水平证明...
使用以下函数部分解决了它
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
它并没有递归地逐级进行,但为了我的目的,这解决了问题
看看这个 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")
是的!你可以用开箱即用的 jycm which has a rendering tool 来区分它。
它使用 LCS、Edit distance 和 Kuhn–Munkres 来区分数组。
这是一个通用示例,其中包含集合中的集合和某些集合中的值更改
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
你可以设置no_pairs=False来得到所有的对。这是一个呈现的例子:
至于这里的例子,你可以这样使用:
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) == {}
奖金,你的值被中断为平原上的坐标,你甚至可以定义一个运算符来
判断两个点是否匹配!(然后比较它们的值)
代码如下:
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
如您所见,jycm 报告了点 {'x': 150, 'y': 150}
和 {'x': 100, 'y': 100}
的添加和删除
距离太远(超过 10)并且其他两个点的值发生变化。
P.S。渲染器演示
请link我回答如果已经回答了, 我的问题是我想获得无序的多级差异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
输出是
[{'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
我的问题是如何让 y 和 z 相等,或者是否存在取决于 JSON 的非顺序的实际差异。
一种无序的字典列表,但我正在寻找 list/dict 的 list/dictionaries 字典的水平证明...
使用以下函数部分解决了它
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
它并没有递归地逐级进行,但为了我的目的,这解决了问题
看看这个 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")
是的!你可以用开箱即用的 jycm which has a rendering tool 来区分它。
它使用 LCS、Edit distance 和 Kuhn–Munkres 来区分数组。
这是一个通用示例,其中包含集合中的集合和某些集合中的值更改
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
你可以设置no_pairs=False来得到所有的对。这是一个呈现的例子:
至于这里的例子,你可以这样使用:
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) == {}
奖金,你的值被中断为平原上的坐标,你甚至可以定义一个运算符来 判断两个点是否匹配!(然后比较它们的值)
代码如下:
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
如您所见,jycm 报告了点 {'x': 150, 'y': 150}
和 {'x': 100, 'y': 100}
的添加和删除
距离太远(超过 10)并且其他两个点的值发生变化。
P.S。渲染器演示