Is it possible to selectively determine when the @JsonFilter annotation gets used at runtime?
I'm getting JsonMappingException exception (see below) when I don't provide the filter.
Background:
I learned from a recent StackOverflow post that I can use @JsonFilter to dynamically filter the bean properties getting serialized. This works great. After adding @JsonFilter("apiFilter")
to my domain class and with the addition of this code in my jax-rs service (using the CXF implementation), I am able to dynamically filter the properties returned by my RESTful API:
// shortened for brevity
FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));
return mapper.filteredWriter(filters).writeValueAsString(user);
The problem is there are different service calls where I don't want to apply the filter at all. In those cases I want to return the entire domain class without filtering any properties. In the case where I just try to return the domain class I'm getting an exception as follows:
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)
I know it's already been answered but for any newcommers Jackson has actually added the ability to not fail on missing filters (JACKSON-650):
You just need to call
SimpleFilterProvider.setFailOnUnknownId(false)
and you won't get this exception.
For Spring Boot / Jackson configuration just add:
@Configuration
public class JacksonConfiguration {
public JacksonConfiguration(ObjectMapper objectMapper) {
objectMapper.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false));
}
}
I think you could trick the filtered writer defining an empty serialize filter for the cases where you want all the properties seralized:
FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.serializeAllExcept(emptySet));
This way, when the engine looks for the "apiFilter" filter defined at the @JsonFilter
anotation, it finds it, but it will not have any effect (as will serialize all the properties).
EDIT
Also, you can call the factory method writer()
instead of 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);
I think this last solution is way cleaner, and indeed better.
I had a similar issue getting the same Exception, but the accepted answer didn't really help in my case. Here's the solution that worked for me:
In my setup I was using a custom JacksonSerializer like this:
@JsonSerialize(using = MyCustomSerializer.class)
private Object someAttribute;
And that serializer was implemented like this:
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);
}
}
}
The problem with this is, that as long as you don't use any filters, it works. It also works if you serialize primitives, so for instance if you use jgen.writeString(..)
. If you use filters, that code is wrong, because the filters are stored somewhere inside of the SerializerProvider
, not in the JsonGenerator
. If in that case you use the jsongenerator directly, a new SerializerProvider, that doesn't know about the filters, is created internally. So instead of the shorter jgen.writeObject(o)
you need to call provider.defaultSerializeValue(o, jgen)
. That will ensure that the filters don't get lost and can be applied.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With