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

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);
        }
    }
相关推荐
麦兜*3 小时前
基于Spring Boot的审计日志自动化解决方案,结合SpEL表达式和AOP技术,实现操作轨迹自动记录,并满足GDPR合规要求
java·jvm·spring boot·后端·spring·spring cloud·maven
孟婆来包棒棒糖~11 小时前
SpringCloude快速入门
分布式·后端·spring cloud·微服务·wpf
她说..15 小时前
MybatisPlus-快速入门
java·spring boot·spring cloud·微服务·mybatis·mybatisplus
是2的10次方啊1 天前
Spring全家桶深度解析:从菜鸟到大神的进阶之路
spring boot·spring·spring cloud
GEM的左耳返1 天前
Java面试全攻略:Spring生态与微服务架构实战
spring boot·redis·spring cloud·微服务·kafka·java面试
java叶新东老师2 天前
三、搭建springCloudAlibaba2021.1版本分布式微服务-springcloud loadbalancer负载均衡
分布式·spring cloud·微服务
GEM的左耳返2 天前
互联网大厂Java面试:微服务与AI技术深度交锋
spring cloud·ai·微服务架构·java面试·rag技术
要开心吖ZSH2 天前
【Spring Cloud Gateway 实战系列】高级篇:服务网格集成、安全增强与全链路压测
spring cloud·微服务·gateway·istio
求知摆渡2 天前
Spring Boot 3.5 + Spring Cloud Stream:邮件发送与幂等实战
java·spring boot·spring cloud
椒哥4 天前
Open feign动态切流实现
java·后端·spring cloud