Python实现json串比对并输出差异结果

  • Post author:
  • Post category:python


主要是利用递归,逐层级、逐次、逐字段比较;可以用于幂等类接口的返回体校验。

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'>"}]
[]



版权声明:本文为weixin_45005677原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。