在项目中如何对Map List等对象序列化及反序列化

发布于:2025-05-08 ⋅ 阅读:(19) ⋅ 点赞:(0)

我们知道,在自定义类中,若想完成序列化必须要实现Serializable接口。

那么在实现后如何进行序列化呢?

一.普通对象

序列化:

1.首先我们要定义一个  序列化所需要的工具类 ObjectMapper 

        //定义序列化所需要的工具类  转化机器
        ObjectMapper objectMapper = new ObjectMapper();

2.定义所要序列化的类  (在这里以我正在做的项目中的一个类为例)

        //定义所要序列化的类   转化原料
        CommonResult<String> result = CommonResult.error(500, "系统错误");

3.调用objectMapper.writeValueAsString()方法序列化为String

        //定义序列化后的类   转化产品
        String str = null;
        //序列化
        try {
            str = objectMapper.writeValueAsString(result);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

这个方法只有一个参数,就是要序列化对象本身

打印结果

 反序列化:

        //反序列化   JsonParser:反序列化所转化的对象   ResolvedType: 所返回的对象的类型
        try {
            CommonResult<String> commonResult1 = objectMapper.readValue(str
                    , CommonResult.class);
            System.out.println(commonResult1);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

这个方法有两个参数

JsonParser:反序列化所转化的对象(str  刚序列化后的产物)  

ResolvedType: 所返回的对象的类型(CommonResult)

返回值就是原本对象 

打印结果:

二.List对象

序列化:

1.定义ObjectMapper (上文已经定义过)

2.初始化所要序列化的List(用于演示)

3.调用objectMapper.writeValueAsString()可见与普通对象无差别

真正的区别在反序列化

        //序列化 List
        List<CommonResult<String>> list = Arrays.asList(
                CommonResult.success("success1"),
                CommonResult.success("success2")
        );
        try {
            str = objectMapper.writeValueAsString(list);
            System.out.println(str);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

反序列化:

因为我们不能保证在每个List中存放的类型一致,所以在objectMapper.readValue()方法的第二个参数(类型)不能保持一致,这里引入一个新参数JavaType,也就是反序列化调用的方法参数

看名字就知道它是用于描述java中类型的,我们看看他怎么用的。

        JavaType javaType = objectMapper.getTypeFactory()
                .constructParametricType(List.class, CommonResult.class);

1.调用objectMapper的类型工厂objectMapper.getTypeFactory()(充当工具)

2.获取JavaType  constructParametricType()

这个方法有多个重载,这里有两个参数:

第一个不会变,要序列化的对象类型(List)

第二个:List中的属性类型(CommonResult)

在Map中由于Map有两个参数所以会有第三个参数(按顺序写即可)

然后调用objectMapper.readValue()即可

        List<CommonResult<String>> result2 = null;
        try {
            result2 = objectMapper.readValue(str, javaType);
            //        System.out.println(result2);
            for(CommonResult<String> a : result2) {
                System.out.println(a.getData());
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

三.Map对象

还是按照前文,注意constructParametricType()方法传入三个参数即可

        //序列化 Map
        Map<Integer, CommonResult<String>> map = new HashMap<>();
        map.put(3,CommonResult.success("success3"));
        map.put(4,CommonResult.success("success4"));
        try {
            str = objectMapper.writeValueAsString(map);
            System.out.println(str);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        //反序列化  先构造一个所需要的参数类型  Map类型  内部是什么类型
        JavaType javaType2 = objectMapper.getTypeFactory()
                .constructParametricType(Map.class, Integer.class, CommonResult.class);
        Map<Integer, CommonResult<String>> result3 = null;
        try {
            result3 = objectMapper.readValue(str, javaType2);
            //        System.out.println(result2);
            System.out.println(map.get(3).getCode());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

四.封装成方法

在上述写过程中try了许多的异常,非常的冗余,我们可以参考一下JacksonJsonParser源码对这个问题解决方法。

在源码部分对List与Map是这样实现的:

    public Map<String, Object> parseMap(String json) {
        return (Map)this.tryParse(() -> {
            return (Map)this.getObjectMapper().readValue(json, MAP_TYPE);
        }, Exception.class);
    }

    public List<Object> parseList(String json) {
        return (List)this.tryParse(() -> {
            return (List)this.getObjectMapper().readValue(json, LIST_TYPE);
        }, Exception.class);
    }

我们发现它并没有进行异常捕获,进入tryParse()方法看一下:

    protected final <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
        try {
            return parser.call();
        } catch (Exception var4) {
            if (check.isAssignableFrom(var4.getClass())) {
                throw new JsonParseException(var4);
            } else {
                ReflectionUtils.rethrowRuntimeException(var4);
                throw new IllegalStateException(var4);
            }
        }
    }

原来是在这里进行了异常处理。通过在tryParse()方法传入lambda表达式然后再call()返回(这一步并没有进行实际业务执行,单纯为了将处理异常这一步统一处理)。再判断调用lambda时不活的异常是否是原业务的异常,如果不是就再爆其他的异常。

我们参照这个解决逻辑,完成一个在项目中使用的JacksonUtil类。

同时注意到在源码部分对ObjectMapper这个一直用的对象进行了单例模式的实现,我们也学习一下。

1.单例模式实现ObjectMapper

    /**
     * 单例模式实现ObjectMapper
     */
    private static final ObjectMapper OBJECT_MAPPER;
    static {
        OBJECT_MAPPER = new ObjectMapper();
    }
    //构造方法私有化
    private JacksonUtil() {
    }
    //获取OBJECT_MAPPER对象
    public ObjectMapper getObjectMapper() {
        return JacksonUtil.OBJECT_MAPPER;
    }

2.解决try catch太多的方法

我们可以直接复制源码部分的tryParse代码

只需把final改为static(方便其他方法调用)

else判断的部分也可以不要(jdk自己对异常的补充,可以不要)

    protected static <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
        try {
            return parser.call();
        } catch (Exception var4) {
            if (check.isAssignableFrom(var4.getClass())) {
                throw new JsonParseException(var4);
            }
            throw new IllegalStateException(var4);
        }
    }

我们可以对这个方法进行封装一下:

    /**
     * 封装tryParse方法  使其固定爆出JacksonException异常
     * @param parser 传入的lambda表达式
     * @param <T>
     * @return
     */
    private final <T> T tryParse(Callable<T> parser) {
        return JacksonUtil.tryParse(parser, JacksonException.class);
    }

3.实现序列化

由于普通对象与List Map的序列化过程几乎相同,所以我们一个方法就可以实现。

    /**
     * 序列化  对普通对象  与  List  都适用
     * @param object
     * @return
     */
    public static String writeValueAsString(Object object) {
        return JacksonUtil.tryParse(() -> {
            return JacksonUtil.getObjectMapper().writeValueAsString(object);
        });
    }

4.实现反序列化

普通对象序列化:

    /**
     * 普通对象反序列化
     * @param content
     * @param valueType
     * @param <T>
     * @return
     */
    public static <T> T readValue(String content, Class<T> valueType) {
        return JacksonUtil.tryParse(() -> {
           return JacksonUtil.getObjectMapper().readValue(content, valueType);
        });
    }

List反序列化:注意将第二个参数类型改为 Class<?>而不是  Class<T>

    /**
     * 反序列化  List
     * @param content
     * @param paramClasses
     * @param <T>
     * @return
     */
    public static <T> T readListValue (String content, Class<?> paramClasses) {
        JavaType javaType = JacksonUtil.getObjectMapper().getTypeFactory()
                .constructParametricType(List.class, paramClasses);
        return JacksonUtil.tryParse(() -> {
           return JacksonUtil.getObjectMapper().readValue(content, javaType);
        });
    }

最后附上测试代码:

    @Test
    void jackUtilTest() {
        CommonResult<String> result = CommonResult.error(500, "系统错误");
        String str = JacksonUtil.writeValueAsString(result);
        System.out.println(str);
        CommonResult<String> commonResult = JacksonUtil.readValue(str, CommonResult.class);
        System.out.println(commonResult);

        //序列化 List
        List<CommonResult<String>> list = Arrays.asList(
                CommonResult.success("success1"),
                CommonResult.success("success2")
        );
        String str1 = JacksonUtil.writeValueAsString(list);
        System.out.println(str1);
        list = JacksonUtil.readListValue(str1, CommonResult.class);
        for(CommonResult<String> a : list) {
            System.out.println(a.getData());
        }
    }


网站公告

今日签到

点亮在社区的每一天
去签到