Jackson – 决定哪些字段被序列化/反序列化
最后更新:2020年2月12日
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")));
}
令人惊讶的是,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 的敏感密码信息。
为此,我们只需将 @JsonIgnore 注解添加到 password 的 getter 上,并通过应用 @JsonProperty 注解到 setter 上来启用该字段的反序列化
@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 选择 哪些字段被序列化/反序列化以及哪些字段在此过程中被忽略 的基本知识,当然还介绍了如何完全控制它。
您还可以通过阅读更多文章来进一步了解 Jackson 2,例如 忽略字段、将 JSON 数组反序列化为 Java 数组或集合。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















