2017-12-28

com.fasterxml.jackson.core 的 Jackson Annotations 2.9.3

蠻常用 Jackson 將 Java 物件轉成 JSON(writeValueAsString),或者將 JSON 轉回 Java 物件(readValue)。
public class Book implements Serializable {

  private String title;
  private int year;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public int getYear() {
    return this.year;
  }

  public void setYear(int year) {
    this.year = year;
  }

  @SuppressWarnings("unchecked")
  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.setYear(2017);
    String json = new ObjectMapper().writeValueAsString(b);
    System.out.println(json); // {"title":"Jackson","year":2017}
    Map<String, String> m = new ObjectMapper().readValue(json, Map.class);
    System.out.println(m); // {title=Jackson, year=2017}
  }
}
但聽說它有非常多特別的 Annotation。

@JsonIgnoreProperties & @JsonIgnore

使用 Jackson 最常見的需求就是,不要輸出某些欄位,可能原因有:安全考量(密碼欄位)、資料量(大物件或者 List / Map)、遞迴參考(會當掉?)以及 Lazy 屬性(Hibernate 未初始化的關聯屬性)。

有四種設定方式。

Class Level - @JsonIgnoreProperties

@JsonIgnoreProperties({
    "contractAmt"
})
public class Book implements Serializable {

  private String title;
  private int contractAmt;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public int getContractAmt() {
    return this.contractAmt;
  }

  public void setContractAmt(int contractAmt) {
    this.contractAmt = contractAmt;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.setContractAmt(1099);
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

Field Level - @JsonIgnore

public class Book8 implements Serializable {

  private String title;
  private int contractAmt;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @JsonIgnore
  public int getContractAmt() {
    return this.contractAmt;
  }

  public void setContractAmt(int contractAmt) {
    this.contractAmt = contractAmt;
  }

  public static void main(String[] args) throws IOException {
    Book8 b = new Book8();
    b.setTitle("Jackson");
    b.setContractAmt(1099);
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

特定 Java Type - @JsonIgnoreType

public class Book implements Serializable {

  private String title;
  private Address address;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public Address getAddress() {
    return this.address;
  }

  public void setAddress(Address address) {
    this.address = address;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

@JsonIgnoreType
class Address {

}
但是當你「摸不到」別人家的 Java type 時,可以改用 Mixin(混搭)的方式 - addMixIn。
public class Book implements Serializable {

  private String title;
  private Date signedDate;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public Date getSignedDate() {
    return this.signedDate;
  }

  public void setSignedDate(Date signedDate) {
    this.signedDate = signedDate;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.setSignedDate(new Date(117, 11, 31));

    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writeValueAsString(b));
    // {"title":"Jackson","signedDate":1514649600000}

    mapper = new ObjectMapper();
    mapper.addMixIn(Date.class, SqlDateMixin.class);
    System.out.println(mapper.writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

@JsonIgnoreType
class SqlDateMixin {

}

Filter - @JsonFilter

@JsonFilter("secret")
public class Book implements Serializable {

  private String title;
  private Date signedDate;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public Date getSignedDate() {
    return this.signedDate;
  }

  public void setSignedDate(Date signedDate) {
    this.signedDate = signedDate;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.setSignedDate(new Date(117, 11, 31));

    ObjectMapper mapper = new ObjectMapper();
    SimpleFilterProvider filters = new SimpleFilterProvider();
    filters.addFilter("secret", SimpleBeanPropertyFilter.serializeAllExcept("signedDate"));
    mapper.setFilterProvider(filters);
    System.out.println(mapper.writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

@JsonProperty & @JsonGetter

預設狀況下,JSON 的屬性名稱是使用 Java Bean 的 property name,@JsonProperty 可以在不修改 Java Bean 的情況下自訂JSON 的屬性名稱,也可以用更為通用的 @JsonGetter 達成一樣的目的。
public class Book implements Serializable {

  private String title;
  private int year;

  @JsonProperty("name")
  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public int getYear() {
    return this.year;
  }

  public void setYear(int year) {
    this.year = year;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.setYear(2017);
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"year":2017,"name":"Jackson"}
  }
}

Include.NON_NULL

不要輸出 null 值的欄位,有三個設定 level:global、class 與 field 。

Global Level - setSerializationInclusion(Include.NON_NULL)

public class Book implements Serializable {

  private String title;
  private String author;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getAuthor() {
    return this.author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    // b.setAuthor("MJ");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson","author":null}
    System.out.println(new ObjectMapper().setSerializationInclusion(Include.NON_NULL).writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

Class Level - @JsonInclude(Include.NON_NULL)

@JsonInclude(Include.NON_NULL)
public class Book implements Serializable {

  private String title;
  private String author;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getAuthor() {
    return this.author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    // b.setAuthor("MJ");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson"}
  }
}

Field Level - @JsonInclude(Include.NON_NULL)

public class Book implements Serializable {

  private String title;
  private String author;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @JsonInclude(Include.NON_NULL)
  public String getAuthor() {
    return this.author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    // b.setAuthor("MJ");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson"}
  }
}
@JsonInclude 雖然名為 include,但實際上卻是用來 exclude 的,Include 除了 NON_NULL,還有 ALWAYS(預設)、NON_ABSENT、NON_EMPTY 與 NON_DEFAULT。

@JsonAnyGetter

預設狀況下,Map 物件會轉成 JSON 物件。
public class Book implements Serializable {

  private String title;
  private Map<String, String> histories = new HashMap<String, String>();

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public Map<String, String> getHistories() {
    return this.histories;
  }

  public void setHistories(Map<String, String> histories) {
    this.histories = histories;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.getHistories().put("rev1", "20170101...");
    b.getHistories().put("rev2", "20170201...");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson","histories":{"rev2":"20170201...","rev1":"20170101..."}}
  }
}
@JsonAnyGetter 可以將 Map 裡的名值對抽出來,有點 flatten 的味道
public class Book implements Serializable {

  private String title;
  private Map<String, String> histories = new HashMap<String, String>();

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @JsonAnyGetter
  public Map<String, String> getHistories() {
    return this.histories;
  }

  public void setHistories(Map<String, String> histories) {
    this.histories = histories;
  }

  public static void main(String[] args) throws IOException {
    Book b = new Book();
    b.setTitle("Jackson");
    b.getHistories().put("rev1", "20170101...");
    b.getHistories().put("rev2", "20170201...");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // {"title":"Jackson","rev2":"20170201...","rev1":"20170101..."}
  }
}

@JsonRawValue

直接輸出原始內容,不去處理 JSON 專用的雙引號,可以用來輸出手打的 JSON 字串
public class Book13 implements Serializable {

  private String title;
  private String json;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @JsonRawValue
  public String getJson() {
    return this.json;
  }

  public void setJson(String json) {
    this.json = json;
  }

  public static void main(String[] args) throws IOException {
    Book13 b = new Book13();
    b.setTitle("Jackson");
    b.setJson("{\"title\":\"Jackson\"}");
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // 一般欄位 - {"title":"Jackson","json":"{\"title\":\"Jackson\"}"}
    // @JsonRawValue - {"title":"Jackson","json":{"title":"Jackson"}}
  }
}

@JsonValue

效果類似 toString(),用該 method 回傳的值表示該 instance,適合用於enum
public class Book14 implements Serializable {

  private String title;
  private BookType type;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public BookType getType() {
    return this.type;
  }

  public void setType(BookType type) {
    this.type = type;
  }

  public static void main(String[] args) throws IOException {
    Book14 b = new Book14();
    b.setTitle("Jackson");
    b.setType(BookType.Computer);
    System.out.println(new ObjectMapper().writeValueAsString(b));
    // 一般欄位 - {"title":"Jackson","type":"Computer"}
    // @JsonValue - {"title":"Jackson","type":"COMPUTER"}
  }
}

enum BookType {
  Computer("COMPUTER"), Science("SCIENCE");

  private String name;

  BookType(String name) {
    this.name = name;
  }

  @JsonValue
  public String getName() {
    return name;
  }
}

@JsonRootName

Jackson 可以透過 SerializationFeature.WRAP_ROOT_VALUE 將自身也輸出,但自身預設為 class name,可以用 @JsonRootName 自訂自身名稱。
@JsonRootName("book")
public class Book15 implements Serializable {

  private String title;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public static void main(String[] args) throws IOException {
    Book15 b = new Book15();
    b.setTitle("Jackson");
    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    System.out.println(mapper.writeValueAsString(b));
    // 一般欄位 - {"Book15":{"title":"Jackson"}}
    // @JsonRootName - {"book":{"title":"Jackson"}}
  }
}

@JsonSerialize

自訂物件輸出的內容。
public class Book16 implements Serializable {

  private String title;
  private Date publishDate;

  public String getTitle() {
    return this.title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public Date getPublishDate() {
    return this.publishDate;
  }

  @JsonSerialize(using = DateSerialize.class)
  public Date getPublishDateFormatted() {
    return this.publishDate;
  }

  public void setPublishDate(Date publishDate) {
    this.publishDate = publishDate;
  }

  public static void main(String[] args) throws IOException {
    Book16 b = new Book16();
    b.setTitle("Jackson");
    b.setPublishDate(new Date());
    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writeValueAsString(b));
    // {"title":"Jackson","publishDate":1514390929846,"publishDateFormatted":2017-12-28 00:08:49}
  }
}

class DateSerialize extends JsonSerializer<Date> {

  private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

  @Override
  public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    gen.writeRawValue(df.format(value));
  }

}
---
---
---

沒有留言:

張貼留言