• Springboot JSON 转换:Jackson篇


    近期想整理一下 Springboot 对于处理 JSON 转换的笔记,想起了 Jackson 是 SpringMVC 默认使用的 JSON 转换器,就从 Jackson 下手,后续用到其他的在整理

    本案例基于 Springboot 2.5.7 单元测试场景下进行

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.8.3version>
    dependency>
    

    在后面的测试中会用到的实体类

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class UserEntity {
        private Integer id;
        private String username;
        private String password;
        private Date birthday;
        private LocalDateTime lastLoginDate;
        private DeptEntity dept;
    }
    
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class DeptEntity {
        private Integer id;
        private String name;
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Result {
    
        private int code;
        private String msg;
        private T data;
    
        public static  Result success(T data) {
            return new Result<>(200, "请求成功", data);
        }
    
    }
    

    IOC 容器中可以直接获取到 Jackson 的 ObjectMapper 实例

    @SpringBootTest
    public class SpringTest {
        @Autowired
        private ObjectMapper mapper;
    }
    

    基础类型转换

    简单来说就是实体类转换,无论是实体类还是实体类嵌套方法都是一样的

    实体类转换

    @Test
    void test() throws JsonProcessingException {
        // 实体类
        DeptEntity dept = new DeptEntity(10001, "部门A");
        // 序列化 writerWithDefaultPrettyPrinter 的作用是美化JSON
        String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(dept);
        // 反序列化
        System.out.println(mapper.readValue(json, DeptEntity.class));
    }
    

    实体类嵌套转换

    @Test
    void test() {
        // 实体类
        Date birthday = new Date();
        LocalDateTime lastLoginDate = LocalDateTime.now();
        DeptEntity dept = new DeptEntity(10001, "部门A");
        UserEntity user = new UserEntity(10001, "用户A", null, birthday, lastLoginDate, dept);
        // 序列化 writerWithDefaultPrettyPrinter 的作用是美化JSON
        String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
        // 反序列化
        System.out.println(mapper.readValue(json, UserEntity.class));
    }
    

    集合类型转换

    集合相比基础类型多个泛型的概念,需要构建类型对象进行转换,简单了解这两种集合就够了,复杂一点的后面会提到

    Collection 集合转换

    @Test
    void test() throws JsonProcessingException {
        // 构建List集合
        List source = CollUtil.newArrayList();
        for (int i = 1; i <= 5; i++) {
            source.add(new DeptEntity(10000 + i, "用户" + i));
        }
        // 序列化
        String json = mapper.writeValueAsString(source);
        // 构建Type对象
        CollectionType type = mapper.getTypeFactory().constructCollectionType(List.class, DeptEntity.class);
        // 反序列化
        List target = mapper.readValue(json, type);
        System.out.println(target);
    }
    

    Map 集合转换

    @Test
    void test() throws JsonProcessingException {
        // 构建List集合
        Map source = MapUtil.newHashMap();
        source.put("aaa", "哈哈");
        source.put("bbb", "呵呵");
        // 序列化
        String json = mapper.writeValueAsString(source);
        // 构建Type对象
        MapLikeType type = mapper.getTypeFactory().constructMapLikeType(HashMap.class, String.class, String.class);
        // 反序列化
        Map target = mapper.readValue(json, type);
        System.out.println(target);
    }
    

    复杂类型转换

    这个部分的功能掌握了,类型转换就基本没啥问题了

    带有泛型的转换

    @Test
    void test() throws JsonProcessingException {
        // 实体类
        Result source = Result.success(new DeptEntity(10001, "部门A"));
        // 序列化
        String json = mapper.writeValueAsString(source);
        // 构建Type对象
        JavaType type = mapper.getTypeFactory().constructParametricType(Result.class, DeptEntity.class);
        // 反序列化
        Result target = mapper.readValue(json, type);
        System.out.println(target.getData().getClass());
        System.out.println(target);
    }
    

    泛型嵌套的转换

    @Test
    void test() throws JsonProcessingException {
        String key = "res";
        // 重头戏来了 泛型嵌套的List集合
        List>> source = CollUtil.newArrayList();
        Map> map = MapUtil.newHashMap();
        Result res = Result.success(new DeptEntity(10001, "部门A"));
        map.put(key, res);
        source.add(map);
        // 序列化
        String json = mapper.writeValueAsString(source);
        // 构建Type对象
        SimpleType stringType = SimpleType.constructUnsafe(String.class);
        JavaType result = mapper.getTypeFactory().constructParametricType(Result.class, DeptEntity.class);
        MapLikeType mapType = mapper.getTypeFactory().constructMapLikeType(HashMap.class, stringType, result);
        CollectionType type = mapper.getTypeFactory().constructCollectionType(List.class, mapType);
        // 反序列化
        List>> target = mapper.readValue(json, type);
        System.out.println(target.get(0).get(key).getData().getClass());
        System.out.println(target.get(0).get(key).getClass());
        System.out.println(target.get(0).getClass());
        System.out.println(target.getClass());
        System.out.println(target);
    }
    

    Jackson 的配置项

    常见的用法是把 Controller 回传给前端的 JSON 进行一些处理,例如时间格式化、忽略 NULL 值等等

    这些配置可以在配置文件中完成,可以重新注入ObjectMapper,也可以使用实体类注解单独配置

    这部分内容用到哪些配置项,想起来就补充,随缘更新

    配置文件

    spring:
      jackson:
        # 格式化日期时使用的时区
        time-zone: GMT+8
        # 格式化
        date-format: yyyy-MM-dd HH:mm:ss.SSS
        # 用于格式化的语言环境
        locale: zh_CN
        serialization:
          # 是否开启格式化输出
          indent_output: false
    

    重新注入 ObjectMapper

    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        // 通过该方法对mapper对象进行设置,所有序列化的对象都将该规则进行序列化
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
        // Include.NON_NULL 属性为NULL 不序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
    

    实体类注解

    注解 作用
    @JsonIgnoreProperties 批量设置转 JSON 时忽略的属性
    @JsonIgnore 转 JSON 时忽略当前属性
    @JsonProperty 修改转换后的 JSON 的属性名
    @JsonFormat 转 JSON 时格式化属性的值
  • 相关阅读:
    华为机试真题实战应用【算法代码篇】-字符串消除(附Java、python和C++代码)
    产品经理需要具备哪些素质?
    tkinter显示图片
    前端面试题之——兼容篇
    【Leetcode】面试题 01.09. 字符串轮转
    德语在线翻译
    计算机网络TCP篇之流量控制
    高保链路分析——一看就会
    Latex | 公式编辑
    计算机网络.第五节课.笔记.以太网、CSMA/CD、VLAN
  • 原文地址:https://www.cnblogs.com/hanzhe/p/16814285.html