json数据是现在程序间通信的主流的数据格式,需要json数据进行校验的地方很多。比如:web后端校验前端传入的json数据是否合法:必填字段是否缺失,字段的类型是否正确等等,接口测试过程中响应数据为json时,判断json数据是否符合预期;如果能有一种方法能参照模板或者预期的数据去判断收到的json数据的字段是否缺失,字段的类型是否正确,并能提示那个字段出错,那将是一种美妙的事,于是燥起来,折腾一个python校验json数据的工具。
具体的思路:
- 将json数据转成字典;
- 通过比较字典来判断json数据是否符合预期;
- 对不符合预期的字典有提示;
- 为了简化代码,采用的是遇到错误就返回的方式;
最终实现的代码如下:
class compare_dict():
"""
字典比较类
判断字典是否满足模板:比较模板字典中的键值是否包含于数据字典中
调用的入口为compare_dict:
def compare(self, tmplate_dict, data_dict, cmp_type=True):
入口方法:判断字典是否满足模板:比较模板字典中的键值是否包含于数据字典中
:param tmplate_dict: 用于比较的模板字典
:param data_dict: 数据字典
:param cmp_type: 比较的类型,为True时判断值相等,为False只判断类型是否相等
:return: 元组:True/False,描述
1.默认为严格比较(cmp_type=True),比较模板字典中的字典在数据字典中必须存在且值相同返回成功
2.cmp_type=False时,比较模板字典中的字典在数据字典中必须存在,字典的类型相同返回成功
3.数据字典中相对于模板字典多的字典不做校验(类似于非必填参数,不校验)
4.在校验的过程中,遇到不一致的会立即返回失败和错误描述;
使用举例:
值类验类型
tmplate_dict = {'cc': 1}
data_dict = {'cc': 2}
obj = compare_dict()
d = obj.compare(tmplate_dict, data_dict, False) --> (True, 'success') #
#值校验
tmplate_dict = {'cc': 1}
data_dict = {'cc': 2}
obj = compare_dict()
d = obj.compare(tmplate_dict, data_dict) --> (False, "错误的索引:['cc'];错误描述:数据字典中值不等于1")#j
#字段缺失
tmplate_dict = {'aa':2,'cc': 1}
data_dict = {'cc': 2}
obj = compare_dict()
d = obj.compare(tmplate_dict, data_dict) -->(False, "错误的索引:['aa'];错误描述:key:aa在数据字典不存在")
tmplate_dict = {'cc': {'dd': [{'ee': 1},{'ee': 1}]}, 'fff':{'dd': [{'ee': 1}],'fff':[{'ee': 2},{'ee': 3}]}}
data_dict = {'cc': {'dd': [{'ee': 1},{'ee': 1}]}, 'fff': {'dd': [{'ee': 1}],'fff':[{'ee': 2},{'ee': 2}]}}
obj = compare_dict()
d = obj.compare(tmp_dict, data_dict,cmp_type=True) --> (False, "错误的索引:['fff']['fff'][1]['ee'];错误描述:数据字典中值不等于3")
"""
def __init__(self):
self.success_tag = True, 'success' #校验成功的返回标识
self.key_list = [] # 用于存储遍历的层级,最后拼接成类似python字典取值的描述信息
def cmp_fun(self, tmp_v, data_v, cmp_type):
"""
值比较,cmp_type为True 判断值相等,反之判断类型相同
:param tmp_v:
:param data_v:
:return:元组 True/False,描述
"""
if cmp_type is True:
if tmp_v == data_v:
return self.success_tag
else:
return False, f'数据字典中值不等于{tmp_v}'
else:
if type(tmp_v) == type(data_v):
return self.success_tag
else:
return False, f'数据字典中值的类型不是:{type(tmp_v)}'
def cmp_item_fun(self, key, tmp_item, data_item, cmp_type):
"""
元素比较函数
:param tmp_item:
:param data_item:
:return:元组 True/False,描述
"""
# 判断key是否存在
if data_item == 'key_is_not_exists_!!!':
return False, f'key:{key}在数据字典不存在'
# 判断值类型是否相同
elif type(tmp_item) != type(data_item):
return False, f'数据字典中的{key}的值类型错误'
# 如果是字典,递归调用 cmp_dict
elif isinstance(tmp_item, dict):
result = self.assert_dict(tmp_item, data_item, cmp_type, tag=key)
if result[0] is False:
return result
# 如果是列表,递归调用元素判断的函数
elif isinstance(tmp_item, list) or isinstance(tmp_item, tuple):
# 判断数据字典是否缺少元素;
if len(tmp_item) > len(data_item):
return False, f'数据字典{key}的值的长度小于模板字典'
for tmp_ii, data_ii,index in zip(tmp_item, data_item,range(len(tmp_item))):
self.key_list[-1].append(str([index]))
result = self.cmp_item_fun(index, tmp_ii, data_ii, cmp_type)
if result[0] is False:
return result
# 比较value的值或者类型是否相同
else:
result = self.cmp_fun(tmp_item, data_item, cmp_type)
if result[0] is False:
return result
#返回成功前清除当前层级数据
if self.key_list[-1]:
self.key_list[-1].pop()
return self.success_tag
def assert_dict(self, tmplate_dict, data_dict, cmp_type, tag=None):
"""
判断数据字典是否满足模板字典,支持递归调用
:param tmplate_dict:
:param data_dict:
:param cmp_type:
:param tag: 递归标识,不为None表示递归调用
:return:元组 True/False,描述
"""
for k, tmp_v in tmplate_dict.items():
if tag is None:
self.key_list.append([str([k])])
else:
self.key_list[-1].append(str([k]))
data_v = data_dict.get(k, 'key_is_not_exists_!!!')
result = self.cmp_item_fun(k, tmp_v, data_v, cmp_type)
if result[0] is False:
break
#成功时清除当次调用添加的层级数据
if tag is None:
self.key_list.pop()
else:
result = self.success_tag
return result
def compare(self, tmplate_dict, data_dict, cmp_type=True):
"""
入口方法:判断字典是否满足模板:比较模板字典中的键值是否包含于数据字典中
:param tmplate_dict: 用于比较的模板字典
:param data_dict: 数据字典
:param cmp_type: 比较的类型,为True时判断值相等,为False只判断类型是否相等
:return: 元组:True/False,描述
"""
result, msg = self.assert_dict(tmplate_dict, data_dict, cmp_type)
if result is True:
return self.success_tag
else:
key_str = "".join(self.key_list[-1])
self.key_list.clear()
return result, f'错误的索引:{key_str};错误描述:{msg}'
if __name__ == "__main__":
tmplate_dict = {'aa':2,'cc': [1,2,3]}
data_dict = {'aa':2,'cc': [1,2,4]}
obj = compare_dict()
d = obj.compare(tmplate_dict, data_dict)
print(d)
使用方法比较简单,参考注释中的例子;
网友评论