«1. Обзор
В этой статье мы рассмотрим различные способы управления сериализацией/десериализацией поля Джексоном или нет.
2. Публичное поле
Самый простой способ убедиться, что поле сериализуемо и десериализуемо, — это сделать его общедоступным.
Давайте объявим простой класс с публичным, пакетно-приватным и приватным
public class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// NO setters or getters
}
Из четырех полей класса только публичное логическое значение будет сериализовано в 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 для использования либо общедоступных полей, либо методов получения/установки для сериализации, или, возможно, включив сериализацию для всех полей:
В следующем тестовом примере проверяется, что все поля-члены (включая непубличные) MyDtoAccessLevel являются сериализуемыми:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
6. Изменение имени свойства при сериализации/десериализации
@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"));
}
Выходя за рамки контроля того, какое поле будет сериализовано или десериализовано, вы также можете контролировать способ сопоставления полей с JSON и обратно. Я описал эту конфигурацию здесь.
7. Игнорирование поля при сериализации или десериализации
После этого руководства у нас есть руководство о том, как полностью игнорировать поле при сериализации и десериализации.
Однако иногда нам нужно игнорировать поле только в одном из них, но не в обоих. Джексон достаточно гибок, чтобы приспособиться и к этому интересному варианту использования.
В следующем примере показан объект User, содержащий конфиденциальную информацию о пароле, которую не следует сериализовать в JSON.
Чтобы этого добиться, мы просто добавляем аннотацию @JsonIgnore к методу получения пароля и включаем десериализацию поля, применяя аннотацию @JsonProperty к методу установки:
Теперь информация о пароле не имеет значения. не будет сериализован в JSON:
@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")));
}
8. Заключение
@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"));
}
В этом руководстве рассматриваются основы того, как Джексон выбирает какое поле сериализуется/десериализуется, а какое игнорируется в процессе и, конечно же, как получить полный контроль над ним.
Вы также можете перейти к следующему шагу в понимании Jackson 2, углубившись в такие статьи, как игнорирование поля, десериализация массива JSON в массив или коллекцию Java.
Реализацию всех этих примеров и фрагментов кода можно найти в моем проекте на github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.
«