美文网首页
python中一种通过转成dict比较,校验json数据的思路

python中一种通过转成dict比较,校验json数据的思路

作者: 逐风细雨 | 来源:发表于2019-12-25 12:12 被阅读0次

json数据是现在程序间通信的主流的数据格式,需要json数据进行校验的地方很多。比如:web后端校验前端传入的json数据是否合法:必填字段是否缺失,字段的类型是否正确等等,接口测试过程中响应数据为json时,判断json数据是否符合预期;如果能有一种方法能参照模板或者预期的数据去判断收到的json数据的字段是否缺失,字段的类型是否正确,并能提示那个字段出错,那将是一种美妙的事,于是燥起来,折腾一个python校验json数据的工具。

具体的思路:

  1. 将json数据转成字典;
  2. 通过比较字典来判断json数据是否符合预期;
  3. 对不符合预期的字典有提示;
  4. 为了简化代码,采用的是遇到错误就返回的方式;

最终实现的代码如下:

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)

使用方法比较简单,参考注释中的例子;

相关文章

网友评论

      本文标题:python中一种通过转成dict比较,校验json数据的思路

      本文链接:https://www.haomeiwen.com/subject/dcyfoctx.html