Джексон - решите, какие поля будут сериализованы / десериализованы
1. обзор
В этой статье мы рассмотрим различные способы, которыми мы можем управлятьif a field is serialized / deserialized by 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. Геттер делает непубличное поле сериализуемым и десериализуемым
Теперь еще один простой способ сделать поле, особенно непубличное поле, сериализуемым, это добавить для него геттер:
public class MyDtoWithGetter {
private String stringValue;
private int intValue;
public String getStringValue() {
return stringValue;
}
}
Теперь мы ожидаем, что полеstringValue будет сериализуемым, а другое частное поле - нет, поскольку у него нет геттера:
@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. Сеттер делает десериализуемым только непубличное поле
Мы увидели, как геттер сделал частное поле как сериализуемым, так и десериализуемым. С другой стороны, установщик помечает только непубличное поле как десериализуемое:
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));
}
И, как мы упоминали, сеттер должен только сделать поле десериализуемым, но не сериализуемым:
@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. Сделать все поля глобально сериализуемыми
В некоторых случаях, когда, например, вы не сможете изменить исходный код напрямую - нам нужно настроить способ, которым Джексон работает с закрытыми полями извне.
Такого рода глобальную конфигурацию можно выполнить на уровне ObjectMapper, включив функциюAutoDetect для использования полейpublic или методов получения / установки для сериализации, или, возможно, включить сериализацию для всех полей:
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. Изменение имени свойства при сериализации / десериализации
Помимо контроля того, какое поле сериализуется или десериализуется, вы также можете использоватьhave control over the way a fields maps to JSON and back. Я рассказалthis configuration here.
7. Игнорировать поле при сериализации или десериализации
Послеthis tutorial у нас есть руководство о том, как полностью игнорировать поле при сериализации и десериализации.
Однако иногда нам нужно игнорировать поле только на одном, но не на обоих. Джексон достаточно гибок, чтобы вместить и этот интересный случай использования.
В следующем примере показан объектUser, который содержит конфиденциальную информацию о пароле, которую не следует сериализовать в JSON.
Чтобы добраться туда, мы просто добавляем аннотацию@JsonIgnore к получателюpassword и включаем десериализацию для поля, применяя аннотацию@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. Заключение
В этом руководстве рассказывается, как Джексон выбираетwhich field is serialized/deserialized and which gets ignoredв процессе, и, конечно же, как получить полный контроль над ним.
Вы также можете перейти к следующему шагу в понимании Джексона 2, углубившись в такие статьи, какignoring a field,deserializing a JSON Array to a Java Array or Collection.
Реализация всех этих примеров и фрагментов кода можно найти вmy github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.