Jackson选择哪个字段被序列化/反序列化
1. 概述
在本文中,我们将探讨我们可以控制一个字段是否被 Jackson 序列化/反序列化的各种方法。
2. public
确保字段可序列化和可反序列化的最简单方法是将其公开。 让我们声明一个带有public、protected和private的简单类
public class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// NO setters or getters
}
在该类的四个字段中,只有 public 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注释,并通过在 setter 上应用*@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"));
}