JSONObject比较中涉及到JSONArray的排序问题的思考

4 篇文章 0 订阅

问题背景:

实战项目中有个接单系统, 我们目前要接受另外一个项目组的接单系统, 我们是重构了, 渠道方的对接 基于该接单系统的老版本, 暂时就说是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年了, 只有第一年 我成长的最快, 做的最开心 ,有时候忠诚的员工伤不起呀 ,加油。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值