主要是利用遞歸,逐層級、逐次、逐字段比較;可以用於冪等類接口的返回體校驗。
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 <class 'tuple'>", 'firstResp': "type is <class 'list'>"}
{'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 <class 'list'>", 'firstResp': "type is <class 'str'>"}
方式二:
[{'field': ':key3', 'error_type': 'field type diff', 'secondResp': "type is <class 'tuple'>", 'firstResp': "type is <class 'list'>"}, {'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 <class 'list'>", 'firstResp': "type is <class 'str'>"}]
[]