Рубрики
Без рубрики

Джексон – Решите, Какие Поля Будут Сериализованы/Десериализованы

Как контролировать, какие поля сериализуются/десериализуются Джексоном, а какие игнорируются.

Автор оригинала: Eugen Paraschiv.

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;
    }
}

Теперь мы ожидаем, что поле string Value будет сериализуемым, а другое частное поле-нет, так как у него нет геттера:

@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;
    }
}

Как вы можете видеть, в поле private int Value на этот раз есть только сеттер. У нас есть способ получить доступ к значению, но это не стандартный геттер.

Процесс разархивирования для 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 fields или 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. Игнорируйте поле при сериализации или десериализации

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

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

В следующем примере показан объект 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 .

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