1. 概述
本文將探討我們如何控制 Jackson 是否對字段進行序列化/反序列化。
2. 公有字段
確保字段既可序列化又可反序列化最簡單的方法就是將其聲明為公有。
讓我們聲明一個簡單的類,其中包含一個公有、一個包私有和私有字段。
public class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// NO setters or getters
}以下是翻譯後的內容:
在類中的四項字段中,只有公共的 booleanValue 字段默認會序列化為 JSON:
@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, not(containsString("stringValue")));
assertThat(dtoAsString, not(containsString("intValue")));
assertThat(dtoAsString, not(containsString("floatValue")));
assertThat(dtoAsString, containsString("booleanValue"));
}3. 使用 Getter 使非公共字段可序列化和反序列化
現在,另一種使字段(尤其是非公共字段)可序列化和反序列化的簡單方法是為其添加一個 Getter:
public class MyDtoWithGetter {
private String stringValue;
private int intValue;
public String getStringValue() {
return stringValue;
}
}我們現在期望 stringValue 字段可序列化,而其他的私有字段則不可序列化,因為該字段沒有 getter 方法:
@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
MyDtoGetter dtoObject = new MyDtoGetter();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, containsString("stringValue"));
assertThat(dtoAsString, not(containsString("intValue")));
}反直覺的是,獲取器也使得私有字段可進行序列化——因為一旦擁有獲取器,該字段就被視為一個屬性。
下面我們來看看它的工作原理:
@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable()
throws JsonProcessingException, JsonMappingException, IOException {
String jsonAsString = "{\"stringValue\":\"dtoString\"}";
ObjectMapper mapper = new ObjectMapper();
MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);
assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}4. 使用 Setter 僅標記非公共字段為可序列化
我們之前看到,Getter 使私有字段同時可序列化和可反序列化。相反,Setter 僅將非公共字段標記為可反序列化:
public class MyDtoWithSetter {
private int intValue;
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public int accessIntValue() {
return intValue;
}
}如您所見,私有 intValue 字段僅提供設置器。雖然我們能夠訪問該值,但這並非標準的獲取器。
對於 intValue 的反序列化過程,應該能夠正常工作:
@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable()
throws JsonProcessingException, JsonMappingException, IOException {
String jsonAsString = "{\"intValue\":1}";
ObjectMapper mapper = new ObjectMapper();
MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);
assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}正如我們之前提到的,設置器(setter)應該只使字段可反序列化,而不應使其可序列化。
@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
MyDtoSetter dtoObject = new MyDtoSetter();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, not(containsString("intValue")));
}5. 全局可序列化所有字段
在某些情況下,例如,你可能無法直接修改源代碼——我們需要配置 Jackson 如何處理外部非公共字段。
這種全局配置可以在 ObjectMapper 級別完成,通過啓用 AutoDetect 函數來使用 public 字段或 getter/setter 方法進行序列化,或者啓用所有字段的序列化。
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);以下測試用例驗證了 MyDtoAccessLevel 的所有成員字段(包括非公共字段)都可序列化:
@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, containsString("stringValue"));
assertThat(dtoAsString, containsString("intValue"));
assertThat(dtoAsString, containsString("booleanValue"));
}6. 修改序列化/反序列化過程中的屬性名稱
除了控制哪些字段進行序列化或反序列化之外,你還可以控制字段如何映射到 JSON 以及反向映射。 我在此處討論了此配置。
7. 忽略序列化或反序列化時的一個字段
按照本教程,我們已經掌握瞭如何完全忽略字段的指南,在序列化和反序列化過程中。
然而,有時我們只需要忽略字段中的一個,而不是兩個。Jackson 足夠靈活,可以適應這個有趣的用例。
以下示例展示了一個 User 對象,其中包含不應序列化到 JSON 的敏感密碼信息。
要實現這一點,我們只需在 password 的 getter 中添加 @JsonIgnore 註解,並使用 @JsonProperty 註解啓用字段的反序列化:
@JsonIgnore
public String getPassword() {
return password;
}
@JsonProperty
public void setPassword(String password) {
this.password = password;
}現在密碼信息將不再序列化到JSON中:
@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User userObject = new User();
userObject.setPassword("thePassword");
String userAsString = mapper.writeValueAsString(userObject);
assertThat(userAsString, not(containsString("password")));
assertThat(userAsString, not(containsString("thePassword")));
}然而,包含密碼的 JSON 將成功地反序列化為 User 對象:
@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect()
throws JsonParseException, JsonMappingException, IOException {
String jsonAsString = "{\"password\":\"thePassword\"}";
ObjectMapper mapper = new ObjectMapper();
User userObject = mapper.readValue(jsonAsString, User.class);
assertThat(userObject.getPassword(), equalTo("thePassword"));
}8. 結論
本教程介紹了 Jackson 如何選擇要進行序列化/反序列化的字段,以及如何忽略某些字段,當然也包括如何完全控制這些過程。
您還可以通過深入研究,例如 忽略字段、將 JSON 數組反序列化為 Java 數組或 Collection 等文章,進一步理解 Jackson 2。