Перейти к основному содержимому

Джексон — Решите, какие поля будут сериализованы/десериализованы

· 5 мин. чтения

Упражнение: Сложение двух чисел

Даны два неотрицательный целых числа в виде непустых связных списков. Их цифры хранятся в обратном порядке. И каждый елемент списка содержить ровно одну цифру. Сложите эти два числа и верните сумму в виде связного списка ...

ANDROMEDA

1. Обзор

В этой статье мы рассмотрим различные способы управления сериализацией/десериализацией поля Джексоном или нет.

2. Публичное поле

Самый простой способ убедиться, что поле сериализуемо и десериализуемо, — это сделать его общедоступным.

Давайте объявим простой класс с общедоступным, пакетно-приватным и приватным

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. Геттер делает непубличное поле сериализуемым и десериализуемым

Теперь еще один простой способ сделать поле — особенно непубличное — сериализуемым — добавить для него геттер:

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 для использования общедоступных полей или методов получения/установки для сериализации, или, возможно, включив сериализацию для всех полей:

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. Игнорировать поле при сериализации или десериализации

После этого руководства у нас есть руководство о том, как полностью игнорировать поле при сериализации и десериализации.

Однако иногда нам нужно игнорировать поле только в одном, но не в обоих. Джексон достаточно гибок, чтобы приспособиться и к этому интересному варианту использования.

В следующем примере показан объект User , который содержит конфиденциальную информацию о пароле, которую не следует сериализовать в JSON.

Чтобы попасть туда, мы просто добавляем аннотацию @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 2, углубившись в такие статьи, как игнорирование поля , десериализация массива JSON в массив или коллекцию Java .

Реализацию всех этих примеров и фрагментов кода можно найти в моем проекте на github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.