问题背景:
实战项目中有个接单系统, 我们目前要接受另外一个项目组的接单系统, 我们是重构了, 渠道方的对接 基于该接单系统的老版本, 暂时就说是s1.0 好了 (,我们新开发的项目就是s2.0了);为了保证我们新项目的正确稳定的运行 , 我们需要异步跑一段时间,尤其是需要关注返回给渠道方的报文, 应该保持 s2.0处理的响应报文 与 s1.0 相同,因此,我们上线了一个小应用,报文比对器,比对器针对项目中的响应报文消息 进行数据比对。
问题:比对的过程中 我们发现, com.alibaba.fastjson.JSONObject的equals方法实现如下:
public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler {
private static final long serialVersionUID = 1L;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private final Map<String, Object> map;
public JSONObject(){
this(DEFAULT_INITIAL_CAPACITY, false);
}
public JSONObject(Map<String, Object> map){
if (map == null) {
throw new IllegalArgumentException("map is null.");
}
this.map = map;
}
public JSONObject(boolean ordered){
this(DEFAULT_INITIAL_CAPACITY, ordered);
}
// 使用了map的equals
public boolean equals(Object obj) {
return this.map.equals(obj);
}
。。。。
}
可以看到 是使用的map.equals(obj)方法来实现的,那么map的equals又是怎么处理的呢 ,贴上代码:
public abstract class AbstractMap<K,V> implements Map<K,V>{
/**
* 省略其他方法 , 这里是抽象类AbstractMap实现的equals方法;
首先对比对象类型,然后对比key 和 value的一致, 重点是 当获取到value以后 ,就需要遍历
调用 value的equals方法了 ,因此 我们需要关注 JSONArray的equals方法
**/
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<K,V> m = (Map<K,V>) o;
if (m.size() != size())
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
}
这里是抽象类AbstractMap实现的equals方法;
首先对比对象类型,然后对比key 和 value的一致, 重点是 当获取到value以后 ,就需要遍历
调用 value的equals方法了 ,因此 我们需要关注 JSONArray的equals方法,因为多数情况下 我们会在jsonobject中嵌套使用jsonArray和jsonObject ,因此需要了解jsonArray的equals
public class JSONArray extends JSON implements List<Object>, Cloneable, RandomAccess, Serializable {
private static final long serialVersionUID = 1L;
private final List<Object> list;
protected transient Object relatedArray;
protected transient Type componentType;
public JSONArray(){
this.list = new ArrayList<Object>();
}
public boolean equals(Object obj) {
return this.list.equals(obj);
}
是的 ,我们看到了 , 直接使用了List#equals方法,跟进去 我们会看到 其实现策略
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
list的equals方法的实现是这样的 , 比对每个位置的对象, 首先序列要一直 , 然后 使用该数组位置的对象 进行 equals,因此我们就可以断定:下面俩实例的equals肯定是不相等的,然而我们在业务中并不care这件事情,因此 如果直接使用jsonObject 或者是jsonArray的equals方法得到的结果 和我们的预期不同, 预期是不关注 response的顺序性, 只要求存在就可以了 ,因此需要想办法,怎么处理这件事情
实例1
{
"clauseLiabilityCodeList": [
"79H_ZXG027",
"79H_ZXG129",
"79J_FXG055",
"79I_FXG054",
"79K_FXG056",
"79L_FXG022",
"79O_FXG057",
"79N_ZXG119",
"79N_ZXG161",
"79N_ZXG159"]
}
实例2
{
"clauseLiabilityCodeList": [
"79I_FXG054",
"79K_FXG056",
"79L_FXG022",
"79O_FXG057",
"79N_ZXG119",
"79N_ZXG161",
"79H_ZXG027",
"79H_ZXG129",
"79J_FXG055",
"79N_ZXG159"]
}
解决办法目前想到三种 :
1,写个工具类,自己实现这种不关注顺序的比较需求,只关注业务字段存在就可以了;
2,排序, 自己写好字段排序和list集合排序,保证数据的顺序;
3,遍历所有的字段,所有的key 和value 手动逐一对比;
解决方案:
使用方案一, 自己写个比对工具吧, 代码粗糙, 凑合看吧 :
public class JSONUtils {
/**
* 检查是否存在
*
* @param array
* @param jsonObject
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static boolean checkContains(JSONArray array, Object obj) {
if (null == array || obj == null) {
return false;
}
for (Object object : array) {
// 类型比对
if (!object.getClass().equals(obj.getClass())) {
continue;
}
// JSONObject 类型
if (object instanceof JSONObject) {
// 对象遍历比对
if (JSONUtils.checkEquals((JSONObject) object, (JSONObject) obj)) {
return true;
}
continue;
}
// list类型
if (object instanceof List) {
if (JSONUtils.checkEquals((List) object, (List) obj)) {
return true;
}
continue;
}
// object
if (object.equals(obj)) {
return true;
}
}
return false;
}
/**
* 检查是否存在
*
* @param collect
* @param obj
* @return
*/
@SuppressWarnings("unchecked")
public static boolean checkEquals(JSONObject json, JSONObject json2) {
// 全是null
if (json2 == null && json == null) {
return true;
}
// 其中一个是null
if (json2 == null || json == null) {
return false;
}
// 遍历key值key 数据不相等
Set<String> keySet = json.keySet();
Set<String> keySet2 = json2.keySet();
if (keySet.size() != keySet2.size()) {
return false;
}
for (String key : keySet) {
Object object = json.get(key);
Object object2 = json2.get(key);
// 空对象的比对
if (object == null || object2 == null) {
if (object == object2) {
continue;
} else {
return false;
}
}
// 非空对象 对比类型
if (object.getClass() != object2.getClass()) {
return false;
}
// 判断是不是jsonObject 对象
if (object instanceof JSONObject) {
if (!checkEquals((JSONObject) object, (JSONObject) object2)) {
return false;
}
continue;
}
// 判断是不是array
if (object instanceof List) {
List<Object> list = (List<Object>) object;
List<Object> list2 = (List<Object>) object2;
if (!checkEquals(list, list2)) {
return false;
}
continue;
}
// 常规对象比对
if (!object.equals(object2)) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
public static boolean checkEquals(List<Object> list, List<Object> list2) {
// 空值判断
if (null == list || list2 == null) {
return list == list2;
}
// 下层级的对象遍历
for (Object obj : list) {
boolean isEquals = false;
for (Object obj2 : list2) {
if (obj == null || obj2 == null) {
if (obj == obj2) {
isEquals = true;
break;
}
return false;
}
// 判断类型
if (!obj.getClass().equals(obj2.getClass())) {
return false;
}
// jsonObject校验
if (obj instanceof JSONObject && obj2 instanceof JSONObject) {
if (checkEquals((JSONObject) obj, (JSONObject) obj2)) {
isEquals = true;
break;
}
// list校验
} else if (obj instanceof List && obj2 instanceof List) {
if (checkEquals((List<Object>) obj, (List<Object>) obj2)) {
isEquals = true;
break;
}
// object equals
} else if (obj.equals(obj2)) {
isEquals = true;
break;
}
}
// 遍历list2 没有找到equals的数据
if (!isEquals) {
return false;
}
}
return true;
}
}
如果有更优雅的做法 告知下 , 我这坨代码真的一点也不想看了, 虽然能用 , 但是总感觉 不太舒服 , 就这样吧, 最近经常加班 , 有点力不从心,年龄大了, 难道就这样过下去吗 ? 总是找不到方向 , 最近做的项目感觉自我没有提升, 来公司3年了, 只有第一年 我成长的最快, 做的最开心 ,有时候忠诚的员工伤不起呀 ,加油。