«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, поэтому его легко импортировать и запускать как есть.

«