1. 概述
在本文中,我們將探討我們如何控制 Jackson 是否序列化/反序列化一個字段 或不。
2. 一個公共字段
確保字段既可序列化又可反序列化最簡單的方法是將其聲明為公共的。讓我們聲明一個簡單的類,其中包含一個公共的、包私有的和私有的字段。
public class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// 無設置器或獲取器
}
在類中的四個字段中,只有公共的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")));
}
反直覺的是,Getter 也會使私有字段可反序列化——因為一旦它有 Getter,該字段就被認為是屬性。
讓我們看看它如何工作:
@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 字段現在只有 Setter。我們仍然有辦法訪問該值,但這並不是標準的 Getter。
對 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 如何選擇要進行序列化/反序列化的字段,以及如何忽略某些字段。當然,它還介紹瞭如何完全控制這些過程。
您還可以通過閲讀如 ignoring a field、deserializing a JSON Array to a Java Array or Collection 等文章,更深入地瞭解 Jackson 2。