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

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

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