Springboot JSON 轉換:Jackson篇

2022-10-21 18:01:18

近期想整理一下 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 時格式化屬性的值