当前位置:网站首页>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)
边栏推荐
- Use and principle of wait notify
- Global and Chinese market of solder wire 2022-2028: Research Report on technology, participants, trends, market size and share
- Common solutions for mobile terminals
- Go learning notes (5) basic types and declarations (4)
- C# wpf 使用DockPanel实现截屏框
- Use and principle of reentrantlock
- 0xc000007b the application cannot start the solution normally (the pro test is valid)
- HDU - 1024 Max Sum Plus Plus(DP)
- More than one file was found with OS independent path ‘lib/armeabi-v7a/libyuv. so‘.
- JDBC common interview questions
猜你喜欢

液压滑环的特点讲解

Dynamic verification of new form items in El form; El form verifies that the dynamic form V-IF does not take effect;

Summary of spanner's paper

Thread process foundation of JUC

Things generated by busybox

JDBC常见面试题

工业导电滑环的应用
![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

Typeorm framework

Explanation of characteristics of hydraulic slip ring
随机推荐
Speed regulation and stroke control based on Ti drv8424 driving stepper motor
Unity drags and modifies scene camera parameters under the editor
Fluentd is easy to use. Combined with the rainbow plug-in market, log collection is faster
How to create a progress bar that changes color according to progress
How to traverse massive data in redis
Multi table operation - foreign key cascade operation
vsCode函数注解/文件头部注解快捷键
El cascade echo failed; El cascader does not echo
3D建模与处理软件简介 刘利刚 中国科技大学
Worried about infringement? Must share copyrightless materials on the website. Don't worry about the lack of materials for video clips
Software intelligence: the "world" and "boundary" of AI sentient beings in AAAs system
Global and Chinese markets for business weather forecasting 2022-2028: Research Report on technology, participants, trends, market size and share
Txncoordsender of cockroachdb distributed transaction source code analysis
Programmers dig "holes" to get rich: if they find a loophole, they will be rewarded 12.72 million yuan
Receiving package install and uninstall events
[excel] column operation, which performs specific column for data in a cell, such as text division by comma, colon, space, etc
Global and Chinese market of digital badge 2022-2028: Research Report on technology, participants, trends, market size and share
Mongodb学习篇:安装后的入门第一课
And search: the suspects (find the number of people related to the nth person)
Daily code 300 lines learning notes day 11