1. 簡介
Gson 是 Google 開發的一個開源 Java 庫,用於簡化對象與 JSON 之間的轉換。它提供高效的序列化和反序列化技術,並支持複雜對象。
諸如 Gson 這樣的庫可以提供對 JSON 的直接映射到 POJO(Java 規範對象)的支持。然而,在序列化和反序列化過程中,有時需要排除特定的屬性。
在本教程中,我們將討論 Gson 庫中常用的兩個關鍵註解:<em @Expose</em> 和 <em @SerializedName</em>。雖然這兩個註解都與屬性的可序列化和可反序列化有關,但它們具有不同的使用場景。
2. Gson 安裝配置
要開始使用 Gson,我們在 Maven 中添加其依賴項:
在 <pom.xml> 文件中:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>3. 在 Gson 中使用 <em @Expose>
Gson 的默認行為是在不指定其他情況的前提下,序列化和反序列化 POJO 類中的所有字段。可以使用 <em @Expose> 註解來覆蓋此行為,並控制特定字段在序列化和反序列化中的包含或排除。
Gson 僅在其 <em serialize 和 <em deserialize 屬性設置為 <em true 的情況下,將帶有 @<em @Expose> 註解的字段包含在輸出的 JSON 中。 默認情況下,<em serialize 或 <em deserialize 屬性的值為 <em true。
讓我們來看一個例子。 我們將使用一個 <em User 類,該類具有 <em id、<em name、<em age 和 <em email 屬性。 電子郵件是敏感信息,因此我們將排除它在輸出 JSON 中的序列化。
public class User {
@Expose
String name;
@Expose
int age;
@Expose(serialize = true, deserialize = false)
long id;
@Expose(serialize = false, deserialize = false)
private String email;
// Constructors, Getters, and Setters
}在上述代碼片段中,我們對 name 和 age 字段進行了 @Expose 註解。 缺少顯式的 serialize 和 deserialize 屬性表明它們默認為 true。
我們還應該注意到,email 屬性的兩個註解都設置為 false。因此,序列化的 JSON 會忽略 email 字段。但是,在創建 GsonBuilder 實例時,應該使用 excludeFieldsWithoutExposeAnnotation() 方法才能實現這一點。
另一方面,對於 id,序列化過程會包含它,而反序列化則會忽略 JSON 中存在的任何 id。
@Test
public void givenUserObject_whenSerialized_thenCorrectJsonProduced() {
User user = new User("John Doe", 30, "[email protected]");
user.setId(12345L);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(user);
// Verify that name, age, and id are serialized, but email is not
assertEquals("{\"name\":\"John Doe\",\"age\":30,\"id\":12345}", json);
}@Test
public void givenJsonInput_whenDeserialized_thenCorrectUserObjectProduced() {
String jsonInput = "{\"name\":\"Jane Doe\",\"age\":25,\"id\":67890,\"email\":\"[email protected]\"}";
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.create();
User user = gson.fromJson(jsonInput, User.class);
// Verify that name and age are deserialized, but email and id are not
assertEquals("Jane Doe", user.name);
assertEquals(25, user.getAge());
assertEquals(0, user.getId()); // id is not deserialized
assertNull(user.getEmail()); // email is not deserialized
}在第一個測試中,我們發現序列化的JSON中不包含email屬性。在第二個單元測試中,我們斷言反序列化的User對象忽略了JSON中的email字段。
4. Gson 中的 @SerializedName 註解
讓我們理解 Gson 中 @SerializedName 註解的使用。當我們創建 Java 類並定義其屬性時,JSON 表示可能需要與類中定義的名稱不同的名稱。 此註解將 POJO 屬性映射到其序列化的 JSON 表示中的特定名稱。
以下是我們的先前示例,現在我們希望 User 的 JSON 表示中 firstName 是字段名稱,而不是 name:
public class User {
@Expose
@SerializedName("firstName")
String name;
}我們的單元測試現在應該斷言 firstName 字段:
@Test
public void givenUserObject_whenSerialized_thenCorrectJsonProduced() {
User user = new User("John Doe", 30, "[email protected]");
user.setId(12345L);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(user);
assertEquals("{\"firstName\":\"John Doe\",\"age\":30,\"id\":12345}", json);
}@SerailizedName 支持一個額外的屬性,即 alternate,它接受一個包含屬性的備用名稱列表,並告知解析器在反序列化時查找其中任何一個值。 此功能在屬性名稱可能因外部或遺留系統而異時非常強大。
讓我們考慮一個使用 fullName 而不是 firstName 的系統。通過正確填充 alternate 屬性,我們可以正確解碼它們。
public class User {
@Expose
@SerializedName(value = "firstName", alternate = { "fullName", "name" })
String name;
}@Test
public void givenJsonWithAlternateNames_whenDeserialized_thenCorrectNameFieldMapped() {
String jsonInput1 = "{\"firstName\":\"Jane Doe\",\"age\":25,\"id\":67890,\"email\":\"[email protected]\"}";
String jsonInput2 = "{\"fullName\":\"John Doe\",\"age\":30,\"id\":12345,\"email\":\"[email protected]\"}";
String jsonInput3 = "{\"name\":\"Alice\",\"age\":28,\"id\":54321,\"email\":\"[email protected]\"}";
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
User user1 = gson.fromJson(jsonInput1, User.class);
User user2 = gson.fromJson(jsonInput2, User.class);
User user3 = gson.fromJson(jsonInput3, User.class);
// Verify that the name field is correctly deserialized from different JSON field names
assertEquals("Jane Doe", user1.getName());
assertEquals("John Doe", user2.getName());
assertEquals("Alice", user3.getName());
}我們能夠正確地從輸入 JSON 報文中反序列化 name 屬性,其中 fullName 和 firstName 是屬性名稱。
5. @SerializedName 和 @Expose 的區別
以下是這兩種註解的主要區別的總結:
| @SerializedName | @Expose |
|---|---|
| 將 Java POJO 字段映射到 JSON 字段名稱 | 指示字段是否應進行序列化或反序列化 |
| 一個 value 屬性是必需的,並且有一個可選的 alternate 屬性可用於使用 | 有兩個可選屬性可用:serialize 和 deserialize |
| 無需任何配置即可工作 | 僅在配置了 GsonBuilder 時才有效,並且需要使用 GsonBuilder.excludeFieldsWithoutExposeAnnotation() |
6. 結論
在本文中,我們瞭解了 @SerializedName 和 @Expose 的工作原理,以及如何利用它們在 Java 中處理 JSON 的序列化和反序列化。我們還強調了兩者之間的主要差異。