近期想整理一下 Springboot 对于处理 JSON 转换的笔记,想起了 Jackson 是 SpringMVC 默认使用的 JSON 转换器,就从 Jackson 下手,后续用到其他的在整理
本案例基于 Springboot 2.5.7 单元测试场景下进行
<!-- SpringMVC默认使用Jacson,只需要引用web启动器即可,无序单独引用Jackson --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Springboot单元测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- Lombok工具类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- Hutool工具类 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.3</version> </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<T> { private int code; private String msg; private T data; public static <T> Result<T> 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"); // 序列化 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); // 序列化 String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user); // 反序列化 System.out.println(mapper.readValue(json, UserEntity.class)); }
集合类型转换
集合转换简单了解这两种就够了,复杂一点的后面会提到
Collection 集合转换
@Test void test() throws JsonProcessingException { // 构建List集合 List<DeptEntity> 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<DeptEntity> target = mapper.readValue(json, type); System.out.println(target); }
Map 集合转换
@Test void test() throws JsonProcessingException { // 构建List集合 Map<String, String> 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<String, String> target = mapper.readValue(json, type); System.out.println(target); }
复杂类型转换
这个部分的功能掌握了,类型转换就基本没啥问题了
带有泛型的转换
@Test void test() throws JsonProcessingException { // 实体类 Result<DeptEntity> source = Result.success(new DeptEntity(10001, "部门A")); // 序列化 String json = mapper.writeValueAsString(source); // 构建Type对象 JavaType type = mapper.getTypeFactory().constructParametricType(Result.class, DeptEntity.class); // 反序列化 Result<DeptEntity> 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<Map<String, Result<DeptEntity>>> source = CollUtil.newArrayList(); Map<String, Result<DeptEntity>> map = MapUtil.newHashMap(); Result<DeptEntity> 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<Map<String, Result<DeptEntity>>> 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 时格式化属性的值 |