主要是利用递归,逐层级、逐次、逐字段比较;可以用于幂等类接口的返回体校验。
class JsonCompare:
def __init__(self, second_data, first_data, debug_model=False):
"""
用于两个json串比对,输出两串json字段差异
:param second_data: 用于比对的新json
:param first_data: 基准json
:param debug_model: 为True时将在控制台输出比对结果,默认不开启
"""
self.compare_result = [] # 存放字段差异
self.compare_error = [] # 存放字段比对异常
self.compare(second_data, first_data)
if debug_model:
for i in self.compare_result:
print(i)
for i in self.compare_error:
print(i)
def compare(self, second_data, first_data, path=''):
try:
if not isinstance(second_data, (list, tuple, dict)):
# 比对非list\tuple\dict类型的数据
if not second_data == first_data:
msg = {
'field': path,
'error_type': 'field value diff',
'secondResp': second_data,
'firstResp': first_data
}
self.compare_result.append(msg)
elif isinstance(second_data, (list, tuple)):
# 如果新数据是list/tuple,则判断老数据是否类型一致;不一致则追加到compare_error
if type(second_data) != type(first_data):
msg = {
'field': path,
'error_type': "field type diff",
'secondResp': f"type is {type(second_data)}",
'firstResp': f"type is {type(first_data)}"
}
self.compare_result.append(msg)
return
if len(second_data) != len(first_data):
msg = {
'field': path,
'error_type': "field indexLength diff",
'secondResp': f"Length of list is {len(second_data)}",
'firstResp': f"Length of list is {len(first_data)}"
}
self.compare_result.append(msg)
for index, value in enumerate(second_data):
try:
if index < len(first_data):
self.compare(
value, first_data[index], f'{path}:{index}')
else:
self.compare(value, {}, f'{path}:{index}')
except Exception as e:
self.compare_error.append(
f'Unknown error: {e.args}')
else:
# 比对值为dict类型数据
if not isinstance(first_data, dict):
msg = {
'field': path,
'error_type': "field type diff",
'secondResp': f"type is {type(second_data)}",
'firstResp': f"type is {type(first_data)}"
}
self.compare_result.append(msg)
return
new_keys = set(second_data.keys())
old_keys = set(first_data.keys())
diff_keys = old_keys - new_keys # 用于检查老数据存在某个key,但新数据不存在。
for key, value in second_data.items():
try:
if key in first_data.keys():
self.compare(
value, first_data[key], f"{path}:{key}")
else:
msg = {
'field': f"{path}:{key}",
'error_type': 'field missing',
'secondResp': value,
'firstResp': f"Field of '{key}' is not exists"
}
self.compare_result.append(msg)
except Exception as e:
self.compare_error.append(
f'Unknown error:{e.args}')
if diff_keys:
for key in diff_keys:
msg = {
'field': f"{path}:{key}",
'error_type': 'field missing',
'secondResp': f"Field of '{key}' is not exists",
'firstResp': first_data[key]
}
self.compare_result.append(msg)
except Exception as e:
self.compare_error.append(
f'Unknown error:{e.args}')
使用方法:
_old_data = {
"key1": "value1",
"key2": "value2",
"key3": [1],
"object": {
"object_key1": "object_value2",
"object_key2": {
"sub_key": "object_key2_sub_value"
},
"object_key3": [
{"list_sub_key1": "xx"},
{"list_sub_key2": "xx"}
]
},
"x": 1.2
}
_new_data = {
"key1": "value1",
"key2": "value2",
"key3": (1,),
"object": {
"object_key1": "object_value1",
"object_key2": {
"sub_key": "object_key2_sub_value",
"sub_key2": {"xx": 1}
},
"object_key3": [
{"list_sub_key1": "xx"},
{"list_sub_key2": []}
]
},
"x": 1.2
}
print("方式一:")
JsonCompare(_new_data, _old_data, debug_model=True)
print("方式二:")
json_compare = JsonCompare(_new_data, _old_data)
print(json_compare.compare_result)
print(json_compare.compare_error)
=======================================================================
方式一:
{'field': ':key3', 'error_type': 'field type diff', 'secondResp': "type is " , 'firstResp': "type is " }
{'field': ':object:object_key1', 'error_type': 'field value diff', 'secondResp': 'object_value1', 'firstResp': 'object_value2'}
{'field': ':object:object_key2:sub_key2', 'error_type': 'field missing', 'secondResp': {'xx': 1}, 'firstResp': "Field of 'sub_key2' is not exists"}
{'field': ':object:object_key3:1:list_sub_key2', 'error_type': 'field type diff', 'secondResp': "type is " , 'firstResp': "type is " }
方式二:
[{'field': ':key3', 'error_type': 'field type diff', 'secondResp': "type is " , 'firstResp': "type is " }, {'field': ':object:object_key1', 'error_type': 'field value diff', 'secondResp': 'object_value1', 'firstResp': 'object_value2'}, {'field': ':object:object_key2:sub_key2', 'error_type': 'field missing', 'secondResp': {'xx': 1}, 'firstResp': "Field of 'sub_key2' is not exists"}, {'field': ':object:object_key3:1:list_sub_key2', 'error_type': 'field type diff', 'secondResp': "type is " , 'firstResp': "type is " }]
[]