JAVA Json解析之json字符串转Map

前言

昨天在修改订单这一块,因为我们可爱的产品大佬加了一个小小的新需求~~“首单”。
哟呵?首单?:针对用户A,如果这个用户有推荐人B,那么B就是A的直接上级。如果C有推荐人D,那么D是A的间接上级。
当用户存在上级(直接和间接),这一单均算是首单。。反正我刚开始是有点懵,一个用户居然可能会有两个首单,返利也不一样。

问题

因为订单走的是消息队列。订单支付完成,返利处理也是走的消息队列,这就难受了~~本地不能打断点调试,只能打日志,在测试服上面查看日志。然后就有一行报了nullPointException异常。反复调试,才发现原来是json字符串转Map出了问题。先看代码!

代码

/**
     * 支付完成时 - 根据用户ID - 检测该订单是否是此用户的首单
     * 首单条件:1.该用户有上级(产生返利)  2.订单中包含RMB商品 3.满足1,2两条件的第一个订单
     */
    @Override
    public Boolean checkIsFirstOrder(Integer orderId, Integer userId) {
        Jedis jedis = RedisPool.getJedis();
        try {
            Boolean firstOrder = Boolean.TRUE;

            // check 用户是否存在
            BsUser user = userMapper.queryUserByInfoId(userId);
            if (user == null) {
                throw new BusiException(E.INVALID_PARAMETER, "用户不存在");
            }

            // check 用户是否有上级
            Integer directParentUserId = userMapper.queryParentId(userId);
            if (directParentUserId == null || directParentUserId == -1) {
                // 没有上级 一定不是首单
                firstOrder = Boolean.FALSE;
                logger.info("checkIsFirstOrder: 用户ID:{},是否是首单:{},info:{}", userId, firstOrder, "没有上级 一定不是首单");
                return firstOrder;
            }

            // 1.check redis中是否存有该用户首单数据
            String firstOrderRedis = jedis.hget(Rkey.ORDER_IS_FIRST_ORDER, userId.toString());
            if (!StringUtils.isEmpty(firstOrderRedis)) {
                // 下面这行代码有问题
                Map<String, Object> info = JsonUtils.transBean2Map(firstOrderRedis);
                // Map<String, Object> info = JsonUtils.toBean(firstOrderRedis);
                Object firstOrderTypeRedis = info.get("firstOrderType");
                if (firstOrderTypeRedis != null) {
                    Integer firstOrderType = (Integer) firstOrderTypeRedis;
                    logger.info("checkIsFirstOrder: 用户ID:{},firstOrderTypeRedis:{}", userId, firstOrderTypeRedis);
                    if (firstOrderType.equals(OrderConst.IS_BOTH_FIRST_ORDER) || firstOrderType.equals(OrderConst.IS_INDIRECT_FIRST_ORDER)) {
                        // 不是首单
                        firstOrder = Boolean.FALSE;
                        logger.info("checkIsFirstOrder: 用户ID:{},是否是首单:{},info:{}", userId, firstOrder, "redis已存在 不是首单");
                    } else if (firstOrderType.equals(OrderConst.IS_DIRECT_FIRST_ORDER)) {
                        // 是首单
                        firstOrder = Boolean.TRUE;
                        logger.info("checkIsFirstOrder: 用户ID:{},是否是首单:{},info:{}", userId, firstOrder, "直接首单已存在 间接首单不存在");
                    }
                }
            } else {
                firstOrder = Boolean.TRUE;
            }

            if (firstOrder) {
                // 2.如果redis 判断是首单 查数据库,再判断一次是否存在首单
                List<BsOrder> orderList = orderMapper.queryFirstOrderByUserId(userId);
                if (orderList != null && !orderList.isEmpty()) {
                    for (BsOrder order : orderList) {
                        if (order.getFirstOrderType().equals(OrderConst.IS_INDIRECT_FIRST_ORDER) || order.getFirstOrderType().equals(OrderConst.IS_BOTH_FIRST_ORDER)) {
                            // 不是首单
                            firstOrder = Boolean.FALSE;
                            logger.info("checkIsFirstOrder: 用户ID:{},是否是首单:{},info:{}", userId, firstOrder, "mysql 已存在 不是首单");
                            return firstOrder;
                        }
                    }

                }
            }

            logger.info("checkIsFirstOrder: 用户ID:{},是否是首单:{}", userId, firstOrder);
            return firstOrder;

        } finally {
            RedisPool.returnJedis(jedis);
        }
    }

分析

1.上面代码第31行,变量firstOrderRedis是Json类型字符串,将字符串转Map,变量info的值是null

Map<String, Object> info = JsonUtils.transBean2Map(firstOrderRedis); 

// firstOrderRedis的值是取redis,值如下面截图:
这里写图片描述
即:

{
  "directFirstOrder": {  //直接首单信息
    "userId": 3454,
    "orderId": 976,
    "firstOrderType": 1
  },
  "firstOrderType": 3, //最终首单类型
  "inDirectFirstOrder": { // 间接首单信息
    "userId": 3454,
    "orderId": 976,
    "firstOrderType": 2
  }
}

我也不成想,就是将这样一个json字符串转map,没成功。。。。。。。。。。。。。。。
百思不得其姐!!

JsonUtils是公司封装的json解析工具类,具体是谁写的,我就不知道了~~ 该类完整代码见我这篇博客:JAVA基础之HashMap转List

我当时调用的是JsonUtils中的transBean2Map这个方法,源码如下:

public static Map<String, Object> transBean2Map(Object obj) {
    if (obj == null) {
        return null;
    }
    Map<String, Object> map = new HashMap<String, Object>();
    try {
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (!key.equals("class")) {
                Method getter = property.getReadMethod();
                Object value = getter.invoke(obj);
                map.put(key, value);
            }
        }
    } catch (Exception e) {
        System.out.println("transBean2Map Error " + e);
    }
    return map;
}

仔细一看transBean2Map这个方法才发现,原来他只是将一个bean,也就是一个有属性的类转成Map。而我调用时,看到这个方法传入的是一个object,以为字符串也可以转成map。字符串用这个方法,转出来是个null。

2.分析下transBean2Map这个方法:

// 很明显,这行代码是得到传进来的这个bean的信息。
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());

Introspector类:
Introspector类为访问目标Jave Bean支持的属性、事件和方法提供了标准方法。该方法可用于工具类(如BeanUtils)中。
对于属性、事件和方法中的每一类信息,Introspector会分别分析目标bean以及其父类,寻找显式或隐式信息并用其构建一个能够全面描述目标bean的BeanInfo对象。
通过调用Introspector.getBeanInfo()方法来获得指定类的bean信息。Java Bean规范允许通过实现BeanInfo接口,定义一个对象来描述bean。为了将BeanInfo与bean关联起来,须遵守如下命名模式:bean信息类的名字必须是将”BeanInfo”添加到bean名字的后面构成。
了解更多关于Introspector类,请阅读这篇博客从Introspector谈Java内省机制

咱们接着看:

// 这里就是从刚刚得到的bean信息中,获取到这个类的属性
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
// 遍历这个类的所有属性
for (PropertyDescriptor property : propertyDescriptors) {
    // 得到属性的名字:如name,sex
    String key = property.getName();
    if (!key.equals("class")) {
        Method getter = property.getReadMethod();
        Object value = getter.invoke(obj);
        // 将属性名,属性值放入map key就是属性名
        map.put(key, value);
    }
}

原来是这样将一个类转换为map的。而我把一个json字符串当做一个bean传入这个方法,相当于,先去获取并遍历String类的所有属性。当然就找不到对应的key - value。返回的值也就是一个null。

后面调用工具类JsonUtilstoBean这个方法:

@SuppressWarnings("unchecked")
public static HashMap<String, Object> toBean(String json) {
    return toBean(json, HashMap.class);
}
public static <T> T toBean(String json, Class<T> clazz) {
    try {
        T bean = (T) mapper.readValue(json, clazz);
        return bean;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

问题就这样解决了。。。toBean()??? 大哥,你取名字能取得再随意一点嘛!谁能想到toBean()返回的竟然是一个HashMap…

总结

咳咳!这一次的总结真是令人感到尴尬呢!
1.不能断章取义,看到方法名理解,不一定这个方法作用就是那样。
2.这一次最老火的就是,因为订单这里走的消息队列。改一行代码,就得重启服务器,去测试。这样这是草鸡麻烦!!!
下次,可以将需要测试的方法独立出来,用postMan调用,手动传入需要传入的值,本地打断点测试。方便得多!

这次,测试花了我太多时间,其实本地打断点测试,几分钟就能找到问题。遇到过坑了,下一次才知道怎么去解决。QAQ

好了,这次的博客,主要是为了记录一下这次我遇到的坑。。可能对大家并没有什么帮助。

收尾

世界真奇怪,最陌生的人:快递、外卖、骗子和老板,反而最常给我们打电话。

猜你喜欢

转载自blog.csdn.net/qq_22638399/article/details/80910671