Сериализация только полей, которые соответствуют пользовательским критериям с Джексоном

Сериализация только полей, которые соответствуют пользовательским критериям с Джексоном

1. обзор

Этот туториал покажет, как мы можемuse Jackson to only serialize a field if it meets a specific, custom criteria.

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

Если вы хотите копнуть глубже и изучитьother cool things you can do with the Jackson 2 - переходите кthe main Jackson tutorial.

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:

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 бытьserialized or not на основе его значения.

Затем мы подключаем этот фильтр к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);

И наконец, мы можем проверить, что полеintValue действительноnot part of the marshalled JSON output:

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

3. Пропускать объекты по условию

А теперь давайте обсудим, как пропускать объекты при сериализации на основе свойстваvalue. Мы пропустим все объекты, у которых свойствоhidden равноtrue:

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

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

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

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

Person Класс:

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

И классAddress:

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() - чтобы убедиться, что в случае Hidable объект является свойством, имя свойства также исключено из 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

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

Первый случай,nothing is hidden:

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

Далееonly address is hidden:

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

Теперьentire person is hidden:

@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 при сериализации сложных объектов с Джексоном.

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

Реализация всех этих примеров и фрагментов кодаcan be found in my github project - это проект на основе Eclipse, поэтому его должно быть легко импортировать и запускать как есть.