实现对象之间的序列化和反序列化

1.什么是序列化?

在项目的开发中,为了让前端更好的分析后端返回的结果,我们一般会将返回的信息进行序列化,序列化就是将返回对象的状态信息转换为一种标准化的格式,方便在网络中传输也方便打印日志时号观察,反序列化就是将序列化后的对象还原成原来的对象信息。

2.传统方式实现序列化

1.1 对Object类进行序列化和反序列化

我们是通过ObjectMapper中提供的writeValueAsString()方法实现序列化,通过readValue方法实现反序列化。

代码示例:

java 复制代码
@SpringBootTest
public class JacksonTest {

    @Test
    void JacksonTest() throws IOException {
        ObjectMapper objectMapper=new ObjectMapper();
        CommonResult<String> commonResult=CommonResult.error(500,"系统错误");
        //序列化
        String str=objectMapper.writeValueAsString(commonResult);
        System.out.println("序列化后:"+str);
        //反序列化
        CommonResult commonResult1 = objectMapper.readValue(str.getBytes(StandardCharsets.UTF_8), CommonResult.class);
        System.out.println("反序列化后:"+commonResult1);
}

运行代码

1.2 对List类进行序列化和反序列化

对List的序列化和对Object类的序列化方法一样,不过List类的反序列化需要多一步操作。

通过readValue方法的原码发现,第二个参数是Class类型。

由于Java的泛型在编译时会进行类型擦除,这意味着在运行时,泛型的具体类型信息只保留原始类型,例如List<CommonResult>中的CommonResult是通过泛型的形式传递的,编译时CommonResult里的具体信息就会被擦除,运行时就会被视为List,此时List类型中的CommonResult参数的信息可能会丢失或者欠缺了,如果直接将序列化后的List类型直接反序列化为原来的数据,可能会导致CommonResult类型信息丢失,从而引发问题。

所以,此时,我们要构建一个参数化类型,即带有泛型参数的具体类型,以上面为例,我们就需要构建一个List<CommonResult>的参数类型,这个通过下图方法实现

代码实现:

java 复制代码
@SpringBootTest
public class JacksonTest {

    @Test
    void JacksonTest() throws IOException {
        ObjectMapper objectMapper=new ObjectMapper();  
        String str;
        //List的序列化
        List<CommonResult<String>> resultList= Arrays.asList(
                CommonResult.success("成功1"),
                CommonResult.success("成功2")
        );
        str=objectMapper.writeValueAsString(resultList);
        System.out.println("序列化后:"+str);

        //List的反序列化
        //构造一个带有泛型参数的具体类型
        JavaType javaType=objectMapper.getTypeFactory().constructParametricType(List.class,CommonResult.class);
        resultList=objectMapper.readValue(str,javaType);
        for(CommonResult result:resultList){
            System.out.println("反序列化后:"+result);
        }
    }

在这种实现方式中,我们需要主动处理writeValueAsString和readValue方法抛出的异常,这意味着我们每次调用这两个方法都要处理这两个方法抛出的异常,这是在有点麻烦切有点不美观,如果我们使用的是catch来捕捉异常,这样代码就会变得很难看。

3.模仿springboot原码中实现序列化和反序列化

通过观察原码发现spring中的parseMap和ParseList都是通过tryParse方法来实现反序列化的,tryParse的原码如下图加分析

所以此时, 我们可以将原码中的tryParse方法复制一份我们模仿实现的代码中,并且用单例模式让ObjectMapper只能被构建一次。

java 复制代码
package com.example.lotterysystem.common.utils;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.json.JsonParseException;
import org.springframework.util.ReflectionUtils;

import java.util.List;
import java.util.concurrent.Callable;

public class JacksonUtil {
    //单例构造要私有Jackson的私有方法
    private JacksonUtil(){

    }

    //单例操作
    private static final ObjectMapper OBJECT_MAPPER;

    static {
        OBJECT_MAPPER=new ObjectMapper();
    }

    private static ObjectMapper getObjectMapper(){
        return OBJECT_MAPPER;
    }

    //有队tryParse方法进一步封装,将tryParse捕获的类型写死
    private static  <T> T tryParse(Callable<T> parser){
        return tryParse(parser, JacksonException.class);
    }

    private 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);
        }
    }

    /**
     * 序列化方法
     * @param object
     * @return
     */
    public static String writeValueAsString(Object object){
        return JacksonUtil.tryParse(()->{
            return JacksonUtil.getObjectMapper().writeValueAsString(object);
        });
    }


    /**
     * 反序列化Object类型数据
     * @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类型
     * @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);
        });
    }
}

测试代码

java 复制代码
@Test
    void JacksonUtilTest(){
        CommonResult<String> result=CommonResult.success("成功");
        String str;
        //序列化
        str= JacksonUtil.writeValueAsString(result);
        System.out.println("序列化后:"+str);
        //反序列化
        result=JacksonUtil.readValue(str,CommonResult.class);
        System.out.println("反序列化后:"+result);

        System.out.println("下面是List序列化");

        //List的序列化
        List<CommonResult<String>> resultList= Arrays.asList(
                CommonResult.success("成功1"),
                CommonResult.success("成功2")
        );
        str=JacksonUtil.writeValueAsString(resultList);
        System.out.println("序列化后:"+str);
        //list的反序列化
        List<CommonResult<String>> results=JacksonUtil.readListValue(str,CommonResult.class);
        for(CommonResult son:results){
            System.out.println("反序列化后:"+son);
        }
    }
相关推荐
Sam-August29 分钟前
【分布式架构实战】Spring Cloud 与 Dubbo 深度对比:从架构到实战,谁才是微服务的王者?
java·spring cloud·dubbo
麦兜*7 小时前
MongoDB 6.0 新特性解读:时间序列集合与加密查询
数据库·spring boot·mongodb·spring·spring cloud·系统架构
echoyu.1 天前
消息队列-初识kafka
java·分布式·后端·spring cloud·中间件·架构·kafka
AAA修煤气灶刘哥1 天前
缓存这「加速神器」从入门到填坑,看完再也不被产品怼慢
java·redis·spring cloud
AAA修煤气灶刘哥1 天前
接口又被冲崩了?Sentinel 这 4 种限流算法,帮你守住后端『流量安全阀』
后端·算法·spring cloud
T_Ghost1 天前
SpringCloud微服务服务容错机制Sentinel熔断器
spring cloud·微服务·sentinel
喂完待续1 天前
【序列晋升】28 云原生时代的消息驱动架构 Spring Cloud Stream的未来可能性
spring cloud·微服务·云原生·重构·架构·big data·序列晋升
惜.己2 天前
Docker启动失败 Failed to start Docker Application Container Engine.
spring cloud·docker·eureka
chenrui3102 天前
Spring Boot 和 Spring Cloud: 区别与联系
spring boot·后端·spring cloud