Jackson 是一個流行的庫,用於將 Java 對象序列化/反序列化為各種文本格式。ObjectMapper類是該庫處理 JSON 格式的主要方式。對於其他格式,我們有它的後代(XmlMapper、YAMLMapper)。由於繼承,我們可以通過單一界面以一致的方式處理所有格式。
下載 jar 文件
在學習示例之前,我們需要下載 Jackson jar 文件並將其連接到 IntelliJ IDEA 中的項目。下面以jackson-databind為例,看看如何搜索需要的文件:
-
轉到Maven 存儲庫網站。
-
在搜索框中輸入“ jackson-databind ”。您將獲得以下內容:
-
第一個搜索結果是我們感興趣的。點擊鏈接。
-
有時可能需要特定版本的庫來確保與項目中其他組件的兼容性。最新版本應該可以工作(在撰寫本課程時,它是 2.13.2.2)。點擊鏈接。
-
在打開的頁面上,您需要“捆綁包”鏈接:
-
使用此鏈接下載 jar 文件。
按照類似的過程,您可以找到並下載其餘必要的 jar 文件:
下載所有必需的文件後,將它們連接到 IntelliJ IDEA 中的項目:
-
打開項目設置(您可以使用Ctrl+Alt+Shift+S組合鍵)。
-
去圖書館。
-
按+,然後按“Java”。選擇所有下載的文件。這是我們最終應該得到的:
-
我們的準備工作到此結束。現在我們可以試用ObjectMapper了。
序列化為 JSON
首先,讓我們將一些對象序列化為 JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
class Book {
public String title;
public String author;
public int pages;
}
public class Solution {
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Good Omens";
book.author = "Pratchett T., Gaiman N.";
book.pages = 383;
ObjectMapper mapper = new ObjectMapper();
String jsonBook = mapper.writeValueAsString(book);
System.out.println(jsonBook);
}
}
運行main會給你這個輸出:
ObjectMapper有很多高級設置。讓我們使用其中之一來使 JSON 字符串更具可讀性。創建後對象映射器對象,執行這條語句:
輸出中的信息保持不變,但現在有縮進和換行符:
“標題”:“好兆頭”,
“作者”:“普拉切特 T.,蓋曼 N.”,
“頁數”:383
}
從 JSON 反序列化
現在讓我們做相反的動作:我們將一個字符串反序列化為一個對象。為了能夠看到程序在做什麼,讓我們重寫Book類中的toString方法:
@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", pages=" + pages +
'}';
}
我們將在主要方法中執行以下操作:
public static void main(String[] args) throws Exception {
String jsonString = "{\"title\":\"Good Omens\",\"author\":\"Pratchett T., Gaiman N.\",\"pages\":383}";
Book book = new ObjectMapper().readValue(jsonString, Book.class);
System.out.println(book);
}
輸出:
readValue方法被重載了——它有很多變體,可以接受一個文件、一個鏈接、各種輸入流等。為簡單起見,我們的示例使用了一個接受 JSON 字符串的變體。
如上所述,ObjectMapper有很多設置。讓我們來看看其中的幾個。
忽略未知屬性
考慮這樣一種情況,JSON 字符串具有Book類中不存在的屬性:
public static void main(String[] args) throws Exception {
String jsonString = """
{
"title" : "Good Omens",
"author" : "Pratchett T., Gaiman N.",
"pages" : 383,
"unknown property" : 42
}""";
ObjectMapper mapper = new ObjectMapper();
Book book = mapper.readValue(jsonString, Book.class);
System.out.println(book);
}
執行這段代碼給我們一個UnrecognizedPropertyException。這是默認行為,但我們可以更改它:
ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
創建時對象映射器對象,我們使用configure方法將相應的設置設置為false。configure方法修改調用它的對象,然後返回同一個對象,所以我們可以用不同的方式進行調用:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
就功能而言,此表示法與前一個表示法一樣。
如果我們現在運行main方法,反序列化將成功,未知屬性將被忽略。
方便的註釋
Jackson 為我們提供了幾個註釋,允許我們以各種方式自定義序列化過程。讓我們來看看一些最有用的:
@JsonIgnore — 此註釋位於序列化/反序列化期間應忽略的元素上方:
class Book {
public String title;
@JsonIgnore
public String author;
public int pages;
}
這裡的作者在序列化期間,字段不會包含在生成的 JSON 中。反序列化後,作者字段將獲得默認值 (null),即使 JSON 具有不同的值。
@JsonFormat — 此批註允許您設置序列化數據的格式。讓我們在Book類中再添加一個字段Date:
class Book {
public String title;
public String author;
public int pages;
public Date createdDate = new Date();
}
序列化後,我們得到以下 JSON:
“標題”:“好兆頭”,
“作者”:“Pratchett T.,Gaiman N.”,
“頁面”:383,
“createdDate”:1649330880788
}
如您所見,日期被序列化為數字。我們將添加註釋並設置格式:
class Book {
public String title;
public String author;
public int pages;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public Date createdDate = new Date();
}
現在序列化的結果是:
“標題”:“好兆頭”,
“作者”:“Pratchett T.,Gaiman N.”,
“頁面”:383,
“createdDate”:“2022-04-07”
}
@JsonProperty — 此註釋允許您更改表示序列化字段的屬性的名稱。您還可以使用此註釋標記方法。如果這樣做,那麼它們的返回值將在序列化期間轉換為 JSON 屬性:
class Book {
@JsonProperty("name")
public String title;
public String author;
public int pages;
@JsonProperty("quotedTitle")
public String getQuotedTitle() {
return "\"" + title + "\"";
}
}
序列化結果:
"author" : "Pratchett T., Gaiman N.",
"pages" : 383,
"name" : "Good Omens",
"quotedTitle" : "\"Good Omens\""
}
@JsonInclude — 使用此註釋,您可以指定序列化字段必須滿足的條件。您可以將其應用於單個領域或整個班級。首先,讓我們嘗試序列化一個帶有未初始化字段的對象:
public class Solution {
public static void main(String[] args) throws Exception {
Book book = new Book();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String jsonBook = mapper.writeValueAsString(book);
System.out.println(jsonBook);
}
}
序列化結果:
“標題”:空,
“作者”:空,
“頁面”:0
}
如果您添加註釋:
@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
public String title;
public String author;
public int pages;
}
然後我們得到這個結果:
“頁面”:0
}
現在不序列化為null的字段。
@JsonPropertyOrder — 此註釋允許您設置字段序列化的順序:
@JsonPropertyOrder({"author", "title", "pages"})
class Book {
public String title;
public String author;
public int pages;
}
序列化結果:
"author" : "Pratchett T., Gaiman N.",
"title" : "Good Omens",
"pages" : 383
}
現在,只需記住如何使用註釋。在本模塊結束時,我們將更好地了解它們,甚至創建我們自己的註釋。
XML 中的序列化和反序列化
如果我們需要序列化為 XML,我們可以使用所有相同的設置和註釋。唯一的區別是映射器對象:
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Good Omens";
book.author = "Pratchett T., Gaiman N.";
book.pages = 383;
ObjectMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String xmlBook = mapper.writeValueAsString(book);
System.out.println(xmlBook);
}
輸出:
<title>好兆頭</title>
<author>Pratchett T., Gaiman N.</author>
<pages>383</pages>
</Book>
XML反序列化:
public static void main(String[] args) throws Exception {
String xmlString = """
<Book>
<title>Good Omens</title>
<author>Pratchett T., Gaiman N.</author>
<pages>383</pages>
</Book>""";
ObjectMapper mapper = new XmlMapper();
Book book = mapper.readValue(xmlString, Book.class);
System.out.println(book);
}
YAML 中的序列化和反序列化
我們像處理 XML 一樣處理 YAML:
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Good Omens";
book.author = "Pratchett T., Gaiman N.";
book.pages = 383;
ObjectMapper mapper = new YAMLMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String yamlBook = mapper.writeValueAsString(book);
System.out.println(yamlBook);
}
輸出:
標題:“好兆頭”
作者:“Pratchett T., Gaiman N.”
頁數:383
YAML 的反序列化:
public static void main(String[] args) throws Exception {
String yamlString = """
---
title: "Good Omens"
author: "Pratchett T., Gaiman N."
pages: 383""";
ObjectMapper mapper = new YAMLMapper();
Book book = mapper.readValue(yamlString, Book.class);
System.out.println(book);
}