JavaBeanToMap

时间:2019-06-12
本文章向大家介绍JavaBeanToMap,主要包括JavaBeanToMap使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

java接口返json时,会有字段为空,客户端不希望有为null的字段。

实现

方法一 使用统一json配置

  • 程序启动方法继承 WebMvcConfigurerAdapter , 重写 configureMessageConverters 。
  • 使用该方法遇到的问题:
      1. 在使用过程中遇到map或者list值出现类似{"$ref":"$.data.list[0].batchInfo"} 的值。
        网上搜索该现象为循环引用,解决方法可使用SerializerFeature.DisableCircularReferenceDetect。
        循环引用:当一个对象包含另一个对象时,fastjson就会把该对象解析成引用。引用是通过$ref标示的,下面介绍一些引用的描述
        "$ref":".." 上一级 "$ref":"@" 当前对象,也就是自引用 "$ref":"$" 根对象" $ref":"$.children.0" 基于路径的引用,相当于 root.getChildren().get(0)
      1. 基础对象(Integer、String、List、Map等)可以不输出null,但java实体类确不知道如何才能输出为{}。另外如果map的key为Integer,输出则也为数字(不使用该方法时json格式化会带引号,即为字符串),postman认为输出不可格式化,这块不知道是否会对客户端产生影响。
  • 由于问题2未解决,故此方法未使用。
public class AppLauncher extends WebMvcConfigurerAdapter {

    public static void main(String[] args) {
        SpringApplication.run(AppLauncher.class);
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters); // 不知道这句有什么用
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
            SerializerFeature.PrettyFormat,
            SerializerFeature.WriteMapNullValue, // 空map转{}
            SerializerFeature.WriteNullNumberAsZero, // 空Integer转0
            SerializerFeature.WriteNullStringAsEmpty, // 空String转""
            SerializerFeature.WriteNullListAsEmpty // 空List转[]
        );
        // 处理中文乱码问题
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(fastMediaTypes);
        fastConverter.setFastJsonConfig(fastJsonConfig);

        //处理字符串, 避免直接返回字符串的时候被添加了引号——不知道这句有什么用
        StringHttpMessageConverter smc = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        converters.add(smc);
     
    }
}

方法二 针对部分可能为空的数据手动转map

  • 该方法比较low,场景使用比较有限。但由于目前背景中,能确定只有几个对象可能为null,其他一定不为null。故可采用。
  • 具体实现方式:1. 初始化赋值。2. 代码层面对map/list赋值过程中注意,如果为空则不赋值。(初始化已经为空map/list了)。3. 部分实体类使用工具类转为map。
  • 使用内省方法处理的工具类如下:
    关于内省和JavaBeanInfo的理解可参考(不是我的文章)
    https://nicky-chen.github.io/2018/03/13/introspector/
public class ConvertUtil {

    /**
     * java对象转map
     */
    public static Map<String, Object> javaBeanToMap(Object obj) {
        if (obj == null) {
            return MapUtils.emptyMap();
        }
        try {
            // 通过intropestor分析出字节码对象的信息beaninfo
            //  如果不想把父类的属性也列出来的话,那getBeanInfo的第二个参数填写父类的信息 
            // BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            // 通过调用getPro....方法获取对象的属性描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            // 网上找的代码中没有判空,点进源码中看了下注释,是可能为空的,所以此处做判空校验
            // 实现类 SimpleBeanInfo 直接返的null,其他实现类返的数组
            if (propertyDescriptors == null || propertyDescriptors.length <= 0) {
                return MapUtils.emptyMap();
            }
            Map<String, Object> map = new HashMap<>(propertyDescriptors.length);
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                // 过滤掉class属性
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value;
                if (getter != null) {
                    value = getter.invoke(obj);
                    if (value == null) {
                        value = MapUtils.emptyMap();
                    }
                } else {
                    value = MapUtils.emptyMap();
                }
                map.put(key, value);
            }
            return map;
        } catch (Exception e) {
            log.error("javaBeanToMap failed : {}", e.getMessage());
            return MapUtils.emptyMap();
        }
    }
}
  • 在这之前用了个比较low的方法,这里面是反射获取的类的所有方法,然后获取属性名的时候是根据get切割来的,众所周知,get方法会把属性名首字母大写,于是又有了把首字母转为小写各种切割拼接,感觉很不友好,所以用了上面的方法。low方法如下:
public class ConvertUtil {

    public static Map<String, Object> javaBeanToMap(Object obj) {
    // 这里map初始化还不能设置初始值,因为不知道设为几,不友好
        Map<String, Object> map = new HashMap<>();
        List<Method> methods = getAllMethods(obj);
        for (Method m : methods) {
            String methodName = m.getName();
            if (methodName.startsWith("get")) {
                try {
                    //获取属性名,首字母小写
                    String propertyName = methodName.substring(3);
                    propertyName = (new StringBuilder()).append(Character.toLowerCase(propertyName.charAt(0)))
                        .append(propertyName.substring(1)).toString();
                    if (Objects.isNull(m.invoke(obj))) {
                        map.put(propertyName, MapUtils.emptyMap());
                    } else {
                        map.put(propertyName, m.invoke(obj));
                    }
                } catch (Exception e) {
                    log.error("javaBeanToMap failed : {}", e.getMessage());
                    return MapUtils.emptyMap();
                }
            }
        }
        return map;
    }

    /**
     * 获取obj中的所有方法
     */
    private static List<Method> getAllMethods(Object obj) {
        List<Method> methods = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        while (!clazz.getName().equals("java.lang.Object")) {
            methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
            clazz = clazz.getSuperclass();
        }
        return methods;
    }
}

原文地址:https://www.cnblogs.com/code-to-world/p/11010369.html