反射造轮子系列:Map集合和实体类对象之间还能这样?

天下皆知美之为美,斯恶已;皆知善之为善,斯不善已。

前言

最近在工作的时候,遇到了这样一个问题,我需要

  1. 将Map集合转换成为一个对应的实体类对象。
  2. 将一个实体类对象转换成为对应的Map集合。

那么,如果是你,你会怎么做呢?好吧,这里我就不卖关子了,用阿里巴巴的Fastjson就可以了。关于阿里巴巴的Fastjson的具体使用,可以查看我的这篇博客:

史上最全的JSON解析之Fastjson

当然,在经过上次用反射造轮子之后,我已经不满足用别人写的工具方法了:

反射造轮子系列:巧用Java反射手写简易版Fastjson

至于如何用Fastjson以及手写工具方法完成map和实体类之间的互相转换。接下来,请容我慢慢介绍!

事前准备

首先,我们当然要先准备一个实体类:

/**
 * @ClassName Person
 * @Description 人类
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 1.0
 */
@Data // lombok的注解,提供getter、setter以及toString方法
@AllArgsConstructor  // lombok的注解,提供全参构造方法
@NoArgsConstructor  // lombok的注解,提供无参构造方法
@Accessors(chain = true) // lombok的注解,开启链式编程
public class Person {

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;
}

当然,以上使用了lombok的注解去生成了一些方法,没有下载lombok插件以及导入lombok依赖的话,是无效的,需要自行生成相应的方法。

其次,我们需要一个工具类,用以调用我们手写的方法,里面已经有一个我自己之前手写的将任意实体类对象转换json字符串的方法

/**
 * @ClassName MyJson
 * @Description 我自己的FastJson
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 2.0
 */
public class MyJson {

    /**
     * 将任意实体类对象转换为JSON字符串
     * @param object
     * @return
     */
    public static final String toJsonString(Object object) throws Exception {

        // 获取反射对象
        Class<?> clazz = object.getClass();

        // 获取反射对象的所有属性,包括私有属性
        Field[] fields = clazz.getDeclaredFields();

        // 创建字符串类,便于拼接出JSON字符串
        StringBuffer stringBuffer = new StringBuffer();
        String str = "";
        stringBuffer.append("{");
        // 遍历所有属性
        for (Field field : fields) {

            // 第一次无须拼接逗号
            if (stringBuffer.length() != 1) {
                stringBuffer.append(",");
            }

            // 给私有属性授权,以便能够对于私有属性操作
            field.setAccessible(true);

            // 获取属性名称
            String name = field.getName();
            // 获取属性值
            Object value = field.get(object);

            // 拼接属性名称
            stringBuffer.append("\"");
            stringBuffer.append(name);
            stringBuffer.append("\"");

            stringBuffer.append(":");

            // 两种判断属性值是否为字符串类型的方式
            Boolean flag = field.getType().isInstance(str);
//            Boolean flag = field.getType().toString().equals("class java.lang.String");
            if (flag && value != null) { // 如果属性的类型为字符串并且不为空,则给属性值加上双引号
                stringBuffer.append("\"");
                stringBuffer.append(value);
                stringBuffer.append("\"");
            } else {
                stringBuffer.append(value);
            }
        }
        stringBuffer.append("}");

        return stringBuffer.toString();
    }
}

实体类转Map

Fastjson

用Fastjson将实体类转换成Map非常的简单。在导入了Fastjson的依赖过后,直接将实体类转换成为JSON字符串,再将JSON字符串转换成为Map即可。

/**
 * @ClassName EntityToMap
 * @Description 用Fasjson将实体类转换成map
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 1.0
 */
public class EntityToMap {
    public static void main(String[] args) {
        // 创建一个实体类
        Person person = new Person("古阙月", 18);
        // 将实体类转换成为json字符串
        String jsonString = JSON.toJSONString(person);
        // 将json字符串转换成map
        HashMap personMap = JSON.parseObject(jsonString, HashMap.class);
        // 输出结果到控制台
        System.out.println(personMap);
    }
}

运行得:
在这里插入图片描述
转换成功!

手写工具方法

话不多说,直接上代码:

   /**
     * 将任意对象转换成所对应的map
     * @param object 对象
     * @return
     */
    public static HashMap entityToMap(Object object) throws Exception {

        // 创建一个map对象
        HashMap<String, Object> ObjMap = new HashMap<String, Object>();

        // 获取反射对象
        Class<?> clazz = object.getClass();

        // 获取反射对象的所有属性,包括公有属性和私有属性
        Field[] fields = clazz.getDeclaredFields();

        // 遍历所有属性
        for (Field field : fields) {

            // 私有属性授权,方便访问
            field.setAccessible(true);

            // 赋值
            ObjMap.put(field.getName(), field.get(object));
        }

        return ObjMap;
    }

测试一下:

/**
 * @ClassName EntityToMap
 * @Description 用自己手写的方法将实体类转换成map
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 1.0
 */
public class EntityToMap2 {
    public static void main(String[] args) throws Exception {
        // 创建一个实体类
        Person person = new Person().setAge(0).setName("古阙月");
        // 将person对象转换成map
        HashMap personMap = MyJson.entityToMap(person);
        // 输出结果到控制台
        System.out.println(personMap);
    }
}

运行得:
在这里插入图片描述
一个方法就搞定了,是不是感觉比之前的Fastjson还要方便?

Map转实体类

Fastjson

同样的,用Fastjson将Map转成实体类也很简单。先将Map转成json字符串,再用反射对象将json字符串转换成对应的实体类即可。

/**
 1. @ClassName MapToEntity
 2. @Description 将map转换成实体类
 3. @Author 古阙月
 4. @Date 2020/9/18
 5. @Version 1.0
 */
public class MapToEntity {
    public static void main(String[] args) {
        // 创建一个map,map的键名和实体类的属性名称一一对应
        HashMap<String, Object> personMap = new HashMap();
        personMap.put("name", "小小");
        personMap.put("age", 17);

        // 将map转换成json字符串
        String jsonString = JSON.toJSONString(personMap);

        // 将json字符串转换成对应的实体类
        Person person = JSON.parseObject(jsonString, Person.class);

        // 输出结果
        System.out.println(person);
    }
}

运行:
在这里插入图片描述
转换成功!!!

手写工具方法

之前将实体类转换成Map的手写工具类写法毋庸置疑,遍历得到实体类的属性名称以及属性值然后再一个个往Map中装值便可。但是如果将Map转换成实体类,其实我们会有两个选择:

  1. 遍历实体类所对应反射对象的所有属性对象,通过属性对象的属性名称来获取Map中的值,再赋值给实体类。
  2. 遍历Map,通过Map的属性名称获取对应的实体类反射对象的属性对象,再赋值给实体类。

方法一

我们先来看第一种,遍历属性对象:

	/**
     * 将map转换成为对应的实体类
     * @param objMap 需要转换的map
     * @param clazz  转换实体类类型的反射对象
     * @param <T>    泛型,反射对象对应的对象类型,也是最后返回的对象类型
     * @return
     */
    public static <T> T mapToEntity(HashMap<String, Object> objMap, Class<T> clazz) throws Exception {

        // 创建实例对象
        T instance = clazz.newInstance();

        // 获取所有的属性对象
        Field[] fields = clazz.getDeclaredFields();
        // 遍历所有的属性对象
        for (Field field : fields) {

            // 私有属性访问授权
            field.setAccessible(true);

            // 获取属性名称
            String name = field.getName();

            // 从map中获取属性值
            Object value = objMap.get(name);

            // 给实例对象赋值
            field.set(instance, value);
        }

        return instance;
    }

测试一下:

/**
 * @ClassName MapToEntity
 * @Description 将map转换成实体类
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 1.0
 */
public class MapToEntity2 {
    public static void main(String[] args) throws Exception {
        // 创建一个map,map的键名和实体类的属性名称一一对应
        HashMap<String, Object> personMap = new HashMap();
        personMap.put("name", "小小");
        personMap.put("age", 17);

        // 将map转换成对应的实体类
        Person person = MyJson.mapToEntity(personMap, Person.class);

        // 输出结果
        System.out.println(person);
    }
}

运行得:
在这里插入图片描述
转换成功!老铁,没毛病!!

方法二

我们再来看第二种方法,遍历Map:

	/**
     * TODO 当map中有实体类没有的字段时,会出异常,需要进行异常处理
     * TODO 好处是:会给你提供警报
     * TODO 坏处是:会给强迫症带来不适
     * 将map转换成为对应的实体类
     * @param objMap 需要转换的map
     * @param clazz  转换实体类类型的反射对象
     * @param <T>    泛型,反射对象对应的对象类型,也是最后返回的对象类型
     * @return
     */
    public static <T> T mapToEntity2(HashMap<String, Object> objMap, Class<T> clazz) throws Exception {

        // 创建实例对象
        T instance = clazz.newInstance();

        // 遍历map
        for (String key : objMap.keySet()) {

            // 获取属性值
            Object value = objMap.get(key);

            try {
                // 获取属性对象
                Field field = clazz.getDeclaredField(key);

                // 私有属性访问授权
                field.setAccessible(true);

                // 赋值
                field.set(instance, value);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return instance;
    }

测试一下:

/**
 * @ClassName MapToEntity
 * @Description 将map转换成实体类
 * @Author 古阙月
 * @Date 2020/9/18
 * @Version 1.0
 */
public class MapToEntity3 {
    public static void main(String[] args) throws Exception {
        // 创建一个map,map的键名和实体类的属性名称一一对应
        HashMap<String, Object> personMap = new HashMap();
        personMap.put("name", "小小");
        personMap.put("age", 17);

        // 将map转换成对应的实体类
        Person person = MyJson.mapToEntity2(personMap, Person.class);

        // 输出结果
        System.out.println(person);
    }
}

运动程序,查看控制台结果:
在这里插入图片描述
发现也非常完美。

那么这两种方法似乎都差不多,到底应该使用哪一种呢?请移步下一章节<小思考>

小思考

其实上述两种方法看似差不多,其实不然,让我们来把需要转换的Map来修改一下:

// 创建一个map,map的键名和实体类的属性名称一一对应
        HashMap<String, Object> personMap = new HashMap();
        personMap.put("name", "小小");
        personMap.put("age", 17);

再给map添加一个键值对:

personMap.put("sex", "女");

运行第一种方法(遍历属性对象集合),得到结果:

在这里插入图片描述
结果和之前不变。其实也没什么问题,毕竟Person类中目前也没有sex这个属性。

我们再来看下第二种方法(遍历Map):

在这里插入图片描述
也是不影响结果的。但是这个时候,我们就会发现控制台会告诉我们实体类中不存在sex这个属性。所以,我们可以很简单的得出结论:

  • 第一种方法很舒适,不会报错。
  • 第二种方法会给你报警,提醒你是不是实体类忘记添加了属性。

至于要用哪种,看你自己咯。

大思考

最后,我们再来一个大的总结:其实经过这样下来,我们似乎发现我们自己封装的工具方法,似乎更加简单。而阿里的Fastjson似乎更麻烦,还要用两个方法才能实现转换的功能。有些同学可能就要忍不住问阿里的工程师了:
在这里插入图片描述
但是如果你看了我的这篇博客,可能就会恍然大悟:

七大设计原则(一):单一职责原则

创作不易,各位客官,给个赞吧!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值