当前位置:网站首页>json数据比较器
json数据比较器
2022-07-01 05:28:00 【hotswwkyo】
json数据比较器
不仅支持对json对象、数组的直接比较,还支持嵌套的复杂json对象和数组的直接比较。
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
''' @Author: 思文伟 '''
import json
class JSONCheckpoint(object):
"""JSON格式数据检查点,支持两个复杂JSON直接比较 JSON 数据写为名称/值对。 名称/值由字段名称构成,后跟冒号和值:{ "name":"Bill Gates" } 在 JSON 中,值必须是以下数据类型之一: 字符串 数字 对象(JSON 对象) 数组 布尔 null Simple usage: alist = [{ "status": "success", "data": { "44131521": [{ "joinno": "201812120000013935", "zzbcode": "44131521", }, { "joinno": "201812120000013936", "zzbcode": "44131521", }, { "joinno": "201812120000013937", "zzbcode": "44131521", }] } }] elist = [{ "status": "success", "data": { "44131521": [{ "joinno": "201812120000013937", "zzbcode": "44131521" }, { "joinno": "201812120000013935", "zzbcode": "44131521", }, { "joinno": "201812120000013936", "zzbcode": "44131521", }] } }] JSONCheckpoint().test(alist, elist, False) """
CONVERT_JSON_TEXT_TO_PYTHON_OBJECT = True
def to_py_obj(self, json_text):
return json.loads(json_text)
def test(self, actual, expected, convert=CONVERT_JSON_TEXT_TO_PYTHON_OBJECT, ignore_position=True, list_strict=True, dict_strict=True):
"""比较两个对象是否相等 Args: - actual: 实际 - expected: 预期 - ignore_position: 控制是调用忽略位置比较函数还是完全相等比较函数 True - 忽略位置 False - 完全相等 - list_strict: ignore_position为True的时候,该参数才有意义 - dict_strict: ignore_position为True的时候,该参数才有意义 """
if convert == self.CONVERT_JSON_TEXT_TO_PYTHON_OBJECT:
expected = self.to_py_obj(expected)
print(self.equals(actual, expected, ignore_position=ignore_position, list_strict=list_strict, dict_strict=dict_strict))
def equals(self, actual, expected, ignore_position=True, list_strict=True, dict_strict=True):
"""比较两个对象是否相等 Args: - actual: 实际 - expected: 预期 - ignore_position: 控制是调用忽略位置比较函数还是完全相等比较函数 True - 忽略位置 False - 完全相等 - list_strict: ignore_position为True的时候,该参数才有意义 - dict_strict: ignore_position为True的时候,该参数才有意义 """
if isinstance(expected, (list, tuple)) and isinstance(actual, (list, tuple)):
if ignore_position:
return self.json_array_equals_ignore_position(actual, expected, list_strict=list_strict, dict_strict=dict_strict)
else:
return self.json_array_equals(actual, expected)
elif isinstance(expected, dict) and isinstance(actual, dict):
if ignore_position:
return self.json_obj_equals_ignore_position(actual, expected, dict_strict=dict_strict, list_strict=list_strict)
else:
return self.json_obj_equals(actual, expected)
elif isinstance(expected, bool) or isinstance(actual, bool): # 任意一个是bool值,则进行bool值比较
return self.all_is_same_bool_value(actual, expected)
elif expected is None or actual is None: # 任意一个是None值,则进行None比较
return self.all_is_none(actual, expected)
else:
return actual == expected
def _group(self, dlist):
"""根据数据类型对列表的子元素分组,分组规则如下: - None 分为一组 - 布尔值 分为一组 - 字符串 整型 浮点型 分为一组 - 列表 元祖 分组一组 - 字典类型分为一组 - 除了上面之外的其他类型分为一组 """
others = []
sublists = []
subdicts = []
nonelist = []
boollist = []
str_int_float = []
for one in dlist:
if one is None:
nonelist.append(one)
elif isinstance(one, bool):
boollist.append(one)
elif isinstance(one, (str, int, float)):
str_int_float.append(one)
elif isinstance(one, (list, tuple)):
sublists.append(one)
elif isinstance(one, dict):
subdicts.append(one)
else:
others.append(one)
return nonelist, boollist, str_int_float, sublists, subdicts, others
def json_obj_equals_ignore_position(self, adict, edict, dict_strict=True, list_strict=True):
"""json对象类型数据相当于python的字典,比较两个字典是否相等,支持嵌套字典和列表元祖 Args: - adict 实际字典 - edict 预期字典 - dict_strict 布尔值,控制字典及其包含的嵌套字典的比较方式, True - 两个字典必须键和值完全一致才相等 False - 只要实际字典的包含有预期字典的键值就认为相等 - list_strict 布尔值,控制列表及其包含的嵌套列表的比较方式, True - 两个列表元素个数必须完全相等,同时元素都一样则认为两个列表相等 False - 只要实际列表包含有预期列表的元素就认为两个列表相等(相当于预期列表的元素是实际列表的元素的子集) Useage: self.json_obj_equals_ignore_position({"a":1,"b":2}, {"a":1,"b":2}) >>> True self.json_obj_equals_ignore_position({"a":1,"b":2}, {"a":1}, dict_strict=False) >>> True self.json_obj_equals_ignore_position({"a":1,"b":{"c":3}}, {"a":1,"b":{"c":3}}) >>> True """
akeys = adict.keys()
ekeys = edict.keys()
if dict_strict:
if len(akeys) != len(ekeys):
return False
for k in ekeys:
if k not in akeys:
return False
else:
v1 = adict[k]
v2 = edict[k]
if isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)):
if not self.json_array_equals_ignore_position(v1, v2, list_strict=list_strict, dict_strict=dict_strict):
return False
elif isinstance(v1, dict) and isinstance(v2, dict):
subresult = self.json_obj_equals_ignore_position(v1, v2, dict_strict=dict_strict, list_strict=list_strict)
if not subresult:
return False
elif isinstance(v1, bool) or isinstance(v2, bool): # 任意一个是bool值,则进行bool值比较
if not self.all_is_same_bool_value(v1, v2):
return False
elif v1 is None or v2 is None: # 任意一个是None值,则进行None比较
if not self.all_is_none(v1, v2):
return False
else:
if v1 != v2:
return False
return True
def _none_equals(self, a_nonelist, e_nonelist, strict=True):
is_equals = True
if strict:
len1 = len(a_nonelist)
len2 = len(e_nonelist)
used = []
if len1 == len2:
for val in e_nonelist:
if val not in used:
used.append(val)
count1 = a_nonelist.count(val)
count2 = e_nonelist.count(val)
if count1 != count2:
is_equals = False
break
else:
is_equals = False
else:
foundindexs = []
for e_none in e_nonelist:
found = False
for i, a_none in enumerate(a_nonelist):
if i not in foundindexs:
if e_none is a_none:
foundindexs.append(i)
found = True
if not found:
is_equals = False
break
return is_equals
_bool_equals = _none_equals
def _basic_data_equals(self, a_data, e_data, strict=True):
is_equals = True
if strict:
used = []
len1 = len(a_data)
len2 = len(e_data)
if len1 == len2:
for val in a_data:
if val not in used:
used.append(val)
count1 = a_data.count(val)
count2 = e_data.count(val)
if count1 != count2:
is_equals = False
break
else:
is_equals = False
else:
found_indexs = []
for e_val in e_data:
found = False
for i, a_val in enumerate(a_data):
if i not in found_indexs:
if e_val == a_val:
found_indexs.append(i)
found = True
break
if not found:
is_equals = False
break
return is_equals
_unknow_type_data_equals = _basic_data_equals
@staticmethod
def is_all_empty_objs(*objs):
empty = True
for obj in objs:
if len(obj) >= 1:
empty = False
break
return empty
@staticmethod
def all_is_same_bool_value(*objs):
truelist = []
falselist = []
non_bool = False
for obj in objs:
if isinstance(obj, bool):
if obj is True:
truelist.append(obj)
else:
falselist.append(obj)
else:
non_bool = True
break
if non_bool:
return False # any object is not boolean
else:
if len(objs) == len(truelist) or len(objs) == len(falselist):
return True
else:
return False
@staticmethod
def all_is_none(*objs):
nonelist = []
for obj in objs:
if obj is None:
nonelist.append(obj)
else:
break
if len(nonelist) == len(objs):
return True
else:
return False
def _json_array_equals_ignore_position(self, source, expected, list_strict=True, dict_strict=True):
""" Args: - source 实际列表中所有是列表的子元素构成的列表 - expected 预期列表中所有是列表的子元素构成的列表 - list_strict 布尔值,控制列表及其包含的嵌套列表的比较方式, True - 两个列表元素个数必须完全相等,同时元素都一样则认为两个列表相等 False - 只要实际列表包含有预期列表的元素就认为两个列表相等(相当于预期列表的元素是实际列表的元素的子集) - dict_strict 布尔值,控制字典及其包含的嵌套字典的比较方式, True - 两个字典必须键和值完全一致才相等 False - 只要实际字典的包含有预期字典的键值就认为相等 """
len1 = len(source)
len2 = len(expected)
founds = []
for a_val in source:
a_len = len(a_val)
for e_val in expected:
e_len = len(e_val)
if a_len == e_len:
if self.json_array_equals_ignore_position(a_val, e_val, list_strict=list_strict, dict_strict=dict_strict):
founds.append(a_val)
break
flen = len(founds)
return (len1 == len2) and (len2 == flen)
def _dictlist_check(self, source, expected, list_strict=True, dict_strict=True):
""" Args: - source 实际列表中所有是字典的子元素构成的列表 - expected 预期列表中所有是字典的子元素构成的列表 - dict_strict 布尔值,控制字典及其包含的嵌套字典的比较方式, True - 两个字典必须键和值完全一致才相等 False - 只要实际字典的包含有预期字典的键值就认为相等 - list_strict 布尔值,控制列表及其包含的嵌套列表的比较方式, True - 两个列表元素个数必须完全相等,同时元素都一样则认为两个列表相等 False - 只要实际列表包含有预期列表的元素就认为两个列表相等(相当于预期列表的元素是实际列表的元素的子集) """
len1 = len(source)
len2 = len(expected)
if len1 == len2:
used_indexs = []
not_found_in_source = []
for edict in expected:
found = False
for index, sdict in enumerate(source):
if index in used_indexs:
continue
if self.json_obj_equals_ignore_position(sdict, edict, dict_strict=dict_strict, list_strict=list_strict):
found = True
used_indexs.append(index)
break
if not found:
not_found_in_source.append(edict)
break
if not_found_in_source:
return False
else:
return True
else:
return False
def printjson(self, *obj, **config):
msg = config.get('msg', '')
printable = [json.dumps(o, ensure_ascii=False) for o in obj]
print(msg + '\n'.join(printable))
def json_array_equals_ignore_position(self, alist, elist, list_strict=True, dict_strict=True):
"""忽略列表元素位置,比较两个列表是否相等,支持列表元素可以是嵌套的数据 Args: - alist 实际列表 - elist 预期列表 - dict_strict 布尔值,控制字典及其包含的嵌套字典的比较方式, True - 两个字典必须键和值完全一致才相等 False - 只要实际字典的包含有预期字典的键值就认为相等 - list_strict 布尔值,控制列表及其包含的嵌套列表的比较方式, True - 两个列表元素个数必须完全相等,同时元素都一样则认为两个列表相等 False - 只要实际列表包含有预期列表的元素就认为两个列表相等(相当于预期列表的元素是实际列表的元素的子集) """
if list_strict:
if len(alist) != len(elist):
return False
a_nonelist, a_boollist, a_basedata, a_array_list, a_dict_list, a_other_list = self._group(alist)
e_nonelist, e_boollist, e_basedata, e_array_list, e_dict_list, e_other_list = self._group(elist)
noneresult = self._none_equals(a_nonelist, e_nonelist, list_strict)
boolresult = self._bool_equals(a_boollist, e_boollist, list_strict)
bresult = self._basic_data_equals(a_basedata, e_basedata, list_strict)
uresult = self._unknow_type_data_equals(a_other_list, e_other_list, list_strict)
r1 = noneresult and boolresult and bresult and uresult
empty = self.is_all_empty_objs(a_array_list, a_dict_list, e_array_list, e_dict_list)
if (not r1) or empty:
return r1
r2 = self._json_array_equals_ignore_position(a_array_list, e_array_list, list_strict=list_strict, dict_strict=dict_strict)
r3 = self._dictlist_check(a_dict_list, e_dict_list, dict_strict=dict_strict, list_strict=list_strict)
return r1 and r2 and r3
def json_obj_equals(self, adict, edict):
"""判断两个json对象是否完全相等 Args: - adict 实际字典 - edict 预期字典 Useage: self.json_obj_equals({"a":1,"b":2}, {"a":1,"b":2}) >>> True self.json_obj_equals({"a":1,"b":2}, {"a":1}) >>> False self.json_obj_equals({"a":1,"b":{"c":3}}, {"a":1,"b":{"c":3}}) >>> True """
akeys = adict.keys()
ekeys = edict.keys()
if len(akeys) != len(ekeys):
return False
for k in ekeys:
if k not in akeys:
return False
else:
v1 = adict[k]
v2 = edict[k]
if isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)):
if not self.json_array_equals(v1, v2):
return False
elif isinstance(v1, dict) and isinstance(v2, dict):
subresult = self.json_obj_equals(v1, v2)
if not subresult:
return False
elif isinstance(v1, bool) or isinstance(v2, bool): # 任意一个是bool值,则进行bool值比较
if not self.all_is_same_bool_value(v1, v2):
return False
elif v1 is None or v2 is None: # 任意一个是None值,则进行None比较
if not self.all_is_none(v1, v2):
return False
else:
if v1 != v2:
return False
return True
def json_array_equals(self, alist, elist):
"""判断json数组是否完全相等,即两个数组的元素个数以及相同位置的元素值必须完全相等,才相等 Args: - alist 实际列表 - elist 预期列表 """
alen = len(alist)
elen = len(elist)
if alen != elen:
return False
for i, v1 in enumerate(elist):
v2 = alist[i]
if isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)):
if not self.json_array_equals(v2, v1):
return False
elif isinstance(v1, dict) and isinstance(v2, dict):
if not self.json_obj_equals(v2, v1):
return False
elif isinstance(v1, bool) or isinstance(v2, bool): # 任意一个是bool值,则进行bool值比较
if not self.all_is_same_bool_value(v1, v2):
return False
elif v1 is None or v2 is None: # 任意一个是None值,则进行None比较
if not self.all_is_none(v1, v2):
return False
else:
if v1 != v2:
return False
return True
class Age(object):
def __init__(self, number):
self._number = number
def __eq__(self, other):
return self._number == other._number
if __name__ == "__main__":
checker = JSONCheckpoint()
# ignore_position=False 比较相等(完全一致)
# 即两个值必须完全相等,如果两个值是列表或字典,那么列表及其嵌套列表的元素个数以及相同位置的元素值必须完全相等,
# 字典及其嵌套的子孙字典的键值必须完全一致,才认为则两个值相等
a = [1, 2, {
"age": 18}]
b = [1, 2, {
"age": 18}]
checker.test(a, b, False, ignore_position=False)
a = {
"age": 18, "friends": ["小明", "小红"]}
b = {
"age": 18, "friends": ["小明", "小红"]}
checker.test(a, b, False, ignore_position=False)
# ignore_position=True, list_strict=True, dict_strict=True
# 忽略数组或者嵌套数组中元素的位置,实际和预期数组的元素必须完全相同,实际和预期字典的键值必须完全一致,则认为相等
a = [1, 2, {
"age": 18}]
b = [2, {
"age": 18}, 1]
checker.test(a, b, False, ignore_position=True, list_strict=True, dict_strict=True)
a = {
"age": 18, "friends": ["小红", "小明"]}
b = {
"age": 18, "friends": ["小明", "小红"]}
checker.test(a, b, False, ignore_position=True, list_strict=True, dict_strict=True)
# ignore_position=True, list_strict=False, dict_strict=True
# 忽略数组或者嵌套数组中元素的位置,实际数组完全包含有预期数组的元素,实际和预期字典的键值必须完全一致,则认为相等
a = [1, 2, {
"age": 18}, 3]
b = [2, {
"age": 18}, 1]
checker.test(a, b, False, ignore_position=True, list_strict=False, dict_strict=True)
a = {
"age": 18, "friends": ["小红", "小明", "小霞"]}
b = {
"age": 18, "friends": ["小明", "小红"]}
checker.test(a, b, False, ignore_position=True, list_strict=False, dict_strict=True)
# ignore_position=True, list_strict=True, dict_strict=False
# 忽略数组或者嵌套数组中元素的位置,实际和预期数组的元素必须完全相同,实际字典完全包含有预期字典的键值,则认为相等
a = [1, 2, {
"age": 18, "name": "小玄"}]
b = [2, {
"age": 18}, 1]
checker.test(a, b, False, ignore_position=True, list_strict=True, dict_strict=False)
a = {
"age": 18, "name": "卡罗拉", "friends": ["小红", "小明"]}
b = {
"age": 18, "friends": ["小明", "小红"]}
checker.test(a, b, False, ignore_position=True, list_strict=True, dict_strict=False)
# ignore_position=True, list_strict=False, dict_strict=False
# 忽略数组或者嵌套数组中元素的位置,实际数组完全包含有预期数组的元素,实际字典完全包含有预期字典的键值,则认为相等
a = [1, 2, {
"age": 18, "name": "小玄"}, 3, 2, False, False, None, None, Age(1), Age(1)]
b = [2, {
"age": 18}, 1, 2, False, None, Age(1)]
checker.test(a, b, False, ignore_position=True, list_strict=False, dict_strict=False)
a = {
"age": 18, "name": "卡罗拉", "friends": ["小红", "小明", "小霞"]}
b = {
"age": 18, "friends": ["小明", "小红"]}
checker.test(a, b, False, ignore_position=True, list_strict=False, dict_strict=False)
边栏推荐
- CockroachDB: The Resilient Geo-Distributed SQL Database 论文阅读笔记
- Receiving package install and uninstall events
- Speed regulation and stroke control based on Ti drv8424 driving stepper motor
- Unity uses SQLite
- 积分商城游戏能够给商家带来什么?怎么搭建积分商城?
- How to meet the requirements of source code confidentiality and source code security management
- Introduction to 3D modeling and processing software Liu Ligang University of science and technology of China
- Global and Chinese market of mainboard 2022-2028: Research Report on technology, participants, trends, market size and share
- Mongodb learning chapter: introduction after installation lesson 1
- Set集合详细讲解
猜你喜欢

Actual combat: basic use of Redux

Use and principle of reentrantlock

How to traverse massive data in redis

Data consistency between redis and database

数字金额加逗号;js给数字加三位一逗号间隔的两种方法;js数据格式化
![Fiber Bragg grating (FBG) notes [1]: waveguide structure and Bragg wavelength derivation](/img/83/97c0c6e23cbb7b4c2b630c217c5206.jpg)
Fiber Bragg grating (FBG) notes [1]: waveguide structure and Bragg wavelength derivation

Actual combat: gateway api-2022.2.13

Fluentd is easy to use. Combined with the rainbow plug-in market, log collection is faster

Mongodb学习篇:安装后的入门第一课

工业导电滑环的应用
随机推荐
[Yugong series] February 2022 Net architecture class 005 ABP vNext Net core web application getting started configuration
Global and Chinese market of 3D design and modeling software 2022-2028: Research Report on technology, participants, trends, market size and share
QT waiting box production
LevelDB源码分析之LRU Cache
Rust hello-word
What things you didn't understand when you were a child and didn't understand until you grew up?
Global and Chinese market of metal oxide semiconductor field effect transistors 2022-2028: Research Report on technology, participants, trends, market size and share
导电滑环短路的原因以及应对措施
Things generated by busybox
How to create a progress bar that changes color according to progress
Copy baby prompt: material cannot be empty. How to solve it?
Ssgssrcsr differences
Lock free concurrency of JUC (leguan lock)
Receiving package install and uninstall events
Rust基础入门之变量绑定与解构
Mongodb learning chapter: introduction after installation lesson 1
Use and principle of reentrantlock
了解 JVM 中几个相关问题 — JVM 内存布局、类加载机制、垃圾回收
QDataStream的简单读写验证
3D建模與處理軟件簡介 劉利剛 中國科技大學