@JsonFilter throws "JsonMappingException: не удается разрешить BeanPropertyFilter"

Можно ли выборочно определить, когда аннотация @JsonFilter будет использоваться во время выполнения?

Я получаю исключение JsonMappingException (см. ниже), когда я не предоставляю фильтр.

Фон:

Я узнал из qaru.site/info/135925/..., что я могу использовать @JsonFilter для динамического фильтрации свойств bean, получающих сериализацию. Это отлично работает. Добавив @JsonFilter("apiFilter") в мой класс домена и добавив этот код в мою службу jax-rs (используя реализацию CXF), я могу динамически фильтровать свойства, возвращаемые моим RESTful API:

// shortened for brevity
FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));

return mapper.filteredWriter(filters).writeValueAsString(user);

Проблема в том, что существуют разные вызовы служб, в которых я вообще не хочу применять фильтр. В таких случаях я хочу вернуть весь класс домена без фильтрации каких-либо свойств. В случае, когда я просто пытаюсь вернуть класс домена, я получаю исключение следующим образом:

Caused by: org.codehaus.jackson.map.JsonMappingException: Can not resolve BeanPropertyFilter with id 'apiFilter'; no FilterProvider configured

at org.codehaus.jackson.map.ser.BeanSerializer.findFilter(BeanSerializer.java:252)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFieldsFiltered(BeanSerializer.java:216)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:140)

Ответ 1

Я думаю, вы могли бы обмануть фильтрованный писатель, определяющий пустой фильтр сериализации для случаев, когда вы хотите, чтобы все свойства были серализованными:

FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.serializeAllExcept(emptySet));

Таким образом, когда движок ищет фильтр apiFilter, определенный в anotation @JsonFilter, он находит его, но он не будет иметь никакого эффекта (как будет сериализовать все свойства).

ИЗМЕНИТЬ Кроме того, вы можете вызвать метод factory writer() вместо filteredWriter():

ObjectWriter writer=null;
if(aplyFilter) {
    FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));
    writer=mapper.filteredWriter(filters);
} else {
   writer=mapper.writer();
}

return writer.writeValueAsString(user);

Я думаю, что это последнее решение стало более чистым и даже лучше.

Ответ 2

Я знаю, что это уже ответили, но для любых новичков Джексон фактически добавил возможность не пропустить недостающие фильтры (JACKSON-650):
Вам просто нужно позвонить SimpleFilterProvider.setFailOnUnknownId(false), и вы не получите это исключение.

Ответ 3

Для конфигурации Spring Boot/Jackson просто добавьте:

@Configuration 
public class JacksonConfiguration { 
    public JacksonConfiguration(ObjectMapper objectMapper) { 
        objectMapper.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false)); 
    } 
}

Ответ 4

У меня была аналогичная проблема с тем же Исключением, но принятый ответ мне действительно не помог. Здесь решение, которое сработало для меня:

В моей настройке я использовал пользовательский JacksonSerializer следующим образом:

@JsonSerialize(using = MyCustomSerializer.class)
private Object someAttribute;

И этот сериализатор был реализован следующим образом:

public class MyCustomSerializer extends JsonSerializer<Object> {
  @Override
  public void serialize(Object o, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
    if (o != null) {
      jgen.writeObject(o);
    }
  }
}

Проблема заключается в том, что до тех пор, пока вы не используете какие-либо фильтры, это работает. Он также работает, если вы сериализуете примитивы, например, если вы используете jgen.writeString(..). Если вы используете фильтры, этот код неверен, потому что фильтры хранятся где-то внутри SerializerProvider, а не в JsonGenerator. Если в этом случае вы используете jsongenerator напрямую, новый SerializerProvider, который не знает об фильтрах, создается внутренне. Поэтому вместо более короткого jgen.writeObject(o) вам нужно вызвать provider.defaultSerializeValue(o, jgen). Это гарантирует, что фильтры не будут потеряны и могут быть применены.