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

Сериализуйте только те поля, которые соответствуют пользовательским критериям с помощью Jackson

Сериализуйте только те поля, которые соответствуют вашим пользовательским критериям, используя Jackson 2.

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

1. Обзор

Этот учебник проиллюстрирует, как мы можем использовать Джексона для сериализации поля только в том случае, если оно соответствует определенным пользовательским критериям .

Например, предположим, что мы хотим сериализовать целочисленное значение только в том случае, если оно положительное, и мы хотим полностью пропустить его, если это не так.

Если вы хотите копнуть глубже и узнать другие интересные вещи, которые вы можете сделать с помощью Jackson 2 – перейдите к основному учебнику Джексона .

2. Используйте фильтр Джексона для управления процессом сериализации

Во-первых, нам нужно определить фильтр для нашей сущности, используя аннотацию @JsonFilter :

@JsonFilter("myFilter")
public class MyDto {
    private int intValue;

    public MyDto() {
        super();
    }

    public int getIntValue() {
        return intValue;
    }

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }
}

Затем нам нужно определить наш пользовательский Фильтр свойств :

PropertyFilter theFilter = new SimpleBeanPropertyFilter() {
   @Override
   public void serializeAsField
    (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
     throws Exception {
      if (include(writer)) {
         if (!writer.getName().equals("intValue")) {
            writer.serializeAsField(pojo, jgen, provider);
            return;
         }
         int intValue = ((MyDtoWithFilter) pojo).getIntValue();
         if (intValue >= 0) {
            writer.serializeAsField(pojo, jgen, provider);
         }
      } else if (!jgen.canOmitFields()) { // since 2.3
         writer.serializeAsOmittedField(pojo, jgen, provider);
      }
   }
   @Override
   protected boolean include(BeanPropertyWriter writer) {
      return true;
   }
   @Override
   protected boolean include(PropertyWriter writer) {
      return true;
   }
};

Этот фильтр содержит фактическую логику , определяющую, будет ли поле intValue сериализовано или нет , на основе его значения.

Затем мы подключаем этот фильтр в ObjectMapper и сериализуем объект:

FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", theFilter);
MyDto dtoObject = new MyDto();
dtoObject.setIntValue(-1);

ObjectMapper mapper = new ObjectMapper();
String dtoAsString = mapper.writer(filters).writeValueAsString(dtoObject);

И, наконец, мы можем проверить, что поле int Value действительно не является частью маршалированного вывода JSON :

assertThat(dtoAsString, not(containsString("intValue")));

3. Пропустить Объекты Условно

Теперь – давайте обсудим, как пропустить объекты при сериализации на основе свойства value . Мы пропустим все объекты, где свойство скрыто является истинным :

3.1. Скрытые классы

Во-первых, давайте взглянем на наш Hidable Интерфейс:

@JsonIgnoreProperties("hidden")
public interface Hidable {
    boolean isHidden();
}

И у нас есть два простых класса, реализующих этот интерфейс Person , Address :

Человек Класс:

public class Person implements Hidable {
    private String name;
    private Address address;
    private boolean hidden;
}

И Адрес Класс:

public class Address implements Hidable {
    private String city;
    private String country;
    private boolean hidden;
}

Примечание: Мы использовали @JsonIgnoreProperties(“hidden”) , чтобы убедиться, что hidden само свойство не включено в JSON

3.2. Пользовательский сериализатор

Далее – вот наш пользовательский сериализатор:

public class HidableSerializer extends JsonSerializer {

    private JsonSerializer defaultSerializer;

    public HidableSerializer(JsonSerializer serializer) {
        defaultSerializer = serializer;
    }

    @Override
    public void serialize(Hidable value, JsonGenerator jgen, SerializerProvider provider)
      throws IOException, JsonProcessingException {
        if (value.isHidden())
            return;
        defaultSerializer.serialize(value, jgen, provider);
    }

    @Override
    public boolean isEmpty(SerializerProvider provider, Hidable value) {
        return (value == null || value.isHidden());
    }
}

Обратите внимание, что:

  • Когда объект не будет пропущен, мы делегируем сериализацию введенному по умолчанию сериализатору.
  • Мы переопределили метод isEmpty() – чтобы убедиться, что в случае, если скрытый объект является свойством, имя свойства также исключается из JSON.

3.3. Использование BeanSerializerModifier

Наконец, нам нужно будет использовать BeanSerializerModifier для введения сериализатора по умолчанию в наш пользовательский HidableSerializer – следующим образом:

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_EMPTY);
mapper.registerModule(new SimpleModule() {
    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanSerializerModifier(new BeanSerializerModifier() {
            @Override
            public JsonSerializer modifySerializer(
              SerializationConfig config, BeanDescription desc, JsonSerializer serializer) {
                if (Hidable.class.isAssignableFrom(desc.getBeanClass())) {
                    return new HidableSerializer((JsonSerializer) serializer);
                }
                return serializer;
            }
        });
    }
});

3.4. Пример вывода

Вот простой пример сериализации:

Address ad1 = new Address("tokyo", "jp", true);
Address ad2 = new Address("london", "uk", false);
Address ad3 = new Address("ny", "usa", false);
Person p1 = new Person("john", ad1, false);
Person p2 = new Person("tom", ad2, true);
Person p3 = new Person("adam", ad3, false);

System.out.println(mapper.writeValueAsString(Arrays.asList(p1, p2, p3)));

И выход есть:

[
    {
        "name":"john"
    },
    {
        "name":"adam",
        "address":{
            "city":"ny",
            "country":"usa"
        }
    }
]

3.5. Тест

Наконец – вот несколько тестовых случаев:

В первом случае ничего не скрыто :

@Test
public void whenNotHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", false);
    Person person = new Person("john", ad, false);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.contains("name"));
    assertTrue(result.contains("john"));
    assertTrue(result.contains("address"));
    assertTrue(result.contains("usa"));
}

Далее, скрыт только адрес :

@Test
public void whenAddressHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", true);
    Person person = new Person("john", ad, false);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.contains("name"));
    assertTrue(result.contains("john"));
    assertFalse(result.contains("address"));
    assertFalse(result.contains("usa"));
}

Теперь весь человек скрыт :

@Test
public void whenAllHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", false);
    Person person = new Person("john", ad, true);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.length() == 0);
}

4. Заключение

Этот тип расширенной фильтрации невероятно мощен и позволяет очень гибко настраивать json при сериализации сложных объектов с помощью Jackson.

Более гибкой, но и более сложной альтернативой было бы использование полностью настраиваемого сериализатора для управления выводом JSON – поэтому, если это решение недостаточно гибкое, возможно, стоит изучить это.

Реализация всех этих примеров и фрагментов кода может быть найдена на GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.