近期想整理一下 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);
}
常見的用法是把 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 時格式化屬性的值 |
作者多數為原創文章 ( 部分轉載已標出 ),目前資歷尚淺文章內描述可能有誤,對此造成的後果深表歉意,如有錯誤還望指正