Jaskson精講第6篇-自定義JsonSerialize與Deserialize實現資料型別轉換

字母哥部落格發表於2020-09-24

Jackson是Spring Boot(SpringBoot)預設的JSON資料處理框架,但是其並不依賴於任何的Spring 庫。有的小夥伴以為Jackson只能在Spring框架內使用,其實不是的,沒有這種限制。它提供了很多的JSON資料處理方法、註解,也包括流式API、樹模型、資料繫結,以及複雜資料型別轉換等功能。它雖然簡單易用,但絕對不是小玩具,更多的內容我會寫成一個系列,5-10篇文章,請您繼續關注我。

本節繼續為大家介紹在Jackson序列化中經常遇到的一些特殊的資料型別,如LocalDateTime 。該如何進行序列化和反序列化。

一、LocalDateTime反序列化異常

首先我們定義一個java POJO實體類,其中關鍵的成員變數時birthDate,我們沒有采用Date資料型別,而是採用了Java8 新的日期型別LocalDateTime ,使用LocalDateTime 的好處我就不多說了,有很多的文章解釋說明。我們把精力放回到Jackson的JSON格式序列化與反序列化內容上來。

@Data
public class PlayerStar4 {
  private String name; //姓名
  private LocalDateTime birthDate; //出生日期
}

下面的程式碼,我們首先定義了一個PlayerStar4類的物件player,然後

  • 使用writeValueAsString方法將player物件序列化為JSON字串jsonString
  • 然後使用readValue方法將JSON字串jsonString ,反序列化為PlayerStar4類的物件
@Test
void testJSON2Object() throws IOException {

  ObjectMapper mapper = new ObjectMapper();

  PlayerStar4 player = new PlayerStar4();
  player.setName("curry");//我並不知道庫裡的生日,這裡是編造的
  player.setBirthDate(LocalDateTime.of(1986,4,5,12,50));

  //將player物件以JSON格式進行序列化為String物件
  String jsonString = mapper.writeValueAsString(player);
  System.out.println(jsonString);

  //將JSON字串反序列化為java物件
  PlayerStar4 curry = mapper.readValue(jsonString, PlayerStar4.class);
  System.out.println(curry);

}

但是上面的程式碼報錯了,從下圖中可以看出

  • 將player物件序列化為JSON字串jsonString 的過程被正常執行了,但是LocalDateTime序列化之後的結果,是圖中”黃框中的黃框“內容。
  • 將JSON字串反序列化的過程報錯了,因為Jackson預設情況下,根本不認識圖中”黃框中的黃框“內容這種LocalDateTime序列化之後的JSON字串資料結構。無法把它反序列化為java物件。

怎麼辦?我們需要自定義序列化及反序列化型別轉換器,有兩種方法

  • 繼承StdConverter類,自定義實現String與LocalDateTime相互轉換
  • 繼承JsonSerializer和JsonDeserializer類,自定義實現String與LocalDateTime相互轉換

二、方法一:繼承StdConverter類

繼承StdConverter類,將LocalDateTime序列化為String資料型別

public class LocalDateTimeToStringConverter extends StdConverter<LocalDateTime, String> {
  static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);

  @Override
  public String convert(LocalDateTime value) {
      return value.format(DATE_FORMATTER);
  }
}

繼承StdConverter類,將String資料型別反序列化為LocalDateTime

public class StringToLocalDatetimeConverter extends StdConverter<String, LocalDateTime> {
  @Override
  public LocalDateTime convert(String value) {
      return LocalDateTime.parse(value, LocalDateTimeToStringConverter.DATE_FORMATTER);
  }
}

自定義的轉換器完成之後,我們就可以在對應的成員變數上,使用@JsonSerialize指定序列化轉換器,@JsonDeserialize指定反序列化轉換器。

  @JsonSerialize(converter = LocalDateTimeToStringConverter.class)
  @JsonDeserialize(converter = StringToLocalDatetimeConverter.class)
  private LocalDateTime birthDate;

然後呼叫第一小節中的測試用例,就不會出現異常了。控制檯列印輸出結果如下,第一行是序列化結果JSON格式字串,第二行是Java 物件的toString()方法的列印結果。

{"name":"curry","birthDate":"1986-4-5 12:50:00"}
PlayerStar4(name=curry, birthDate=1986-04-05T12:50)

三、方法二:繼承JsonSerializer和JsonDeserializer類

繼承JsonSerializer<LocalDateTime>類,將LocalDateTime序列化為String資料型別

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
  static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);

  @Override
  public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider)
          throws IOException {
          String s = value.format(DATE_FORMATTER);
          gen.writeString(s);
  }
}

繼承JsonDeserializer<LocalDateTime>類,將String資料型別反序列化為LocalDateTime

public class LocalDatetimeDeserializer extends JsonDeserializer<LocalDateTime> {

  @Override
  public LocalDateTime deserialize(JsonParser p, DeserializationContext ctx)
          throws IOException {
      String str = p.getText();
      return LocalDateTime.parse(str, LocalDateTimeSerializer.DATE_FORMATTER);
  }
}

四、如果上面的你都沒看懂

對於相對小白的讀者,上面的自定義序列化及反序列化轉換過程你都沒懂,對於LocalDateTime的異常你也不要慌,Jackson已經給出瞭解決方案。

需要特別和大家強調的是LocalDateTimeSerializer和LocalDateTimeDeserializer其實並不需要我們自己去定義,因為Jackson已經幫我們定義好了。 之所以我還做了自定義的實現的介紹,是因為要為大家講解這個自定義序列化和反序列化型別轉換的實現過程,以後你再遇到其他的特殊的資料型別轉換,或者LocalDateTime型別的特殊日期格式等,都可以自己來定義JsonSerialize和JsonDeserialize來實現資料型別的轉換。

下面的這兩個類就是Jackson已經幫我們定義好的LocalDateTimeSerializer和LocalDateTimeDeserializer。

import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;

使用方法是在對應的成員變數上,使用@JsonSerialize指定序列化轉換器,@JsonDeserialize指定反序列化轉換器。

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime birthDate;

執行之後的序列化和反序列化結果,和方法一、方法二自定義的實現效果是一樣的。

歡迎關注我的部落格,裡面有很多精品合集

  • 本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力! 。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章