Filtering out properties with Jackson is pretty simple:
final FilterProvider filters = new SimpleFilterProvider().
addFilter(... the name of the filter ...,
SimpleBeanPropertyFilter.filterOutAllExcept(... enumeration of properties ...));
<object mapper>.writer(filters).writeValueAsString(... the bean ...);
I am trying to integrate this in my Jersey REST application. The API user has the possibility to filter the properties by providing a query string:
https://the-api/persons?fields=name,age,location,gender
What is the most elegant way to do this in Jersey? I could easily execute the above in my resource methods, but this somehow kills the elegance of Jersey. Also, I believe that creating a new ObjectMapper for every request will have performance penalties.
I could write a MessageBodyWriter
which fetches the fields
query parameter from the UriInfo
context and serializes the entity to json while applying a filter based on the fields
query parameter. Is this the best way to do this? Like this:
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JustTesting implements MessageBodyWriter<Object> {
@Context
UriInfo uriInfo;
@Context
JacksonJsonProvider jsonProvider;
public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return MediaType.APPLICATION_JSON_TYPE.equals(mediaType);
}
public long getSize(Object object, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return -1;
}
public void writeTo(Object object, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> stringObjectMultivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException {
final FilterProvider filters = new SimpleFilterProvider().addFilter("MyFilter", SimpleBeanPropertyFilter.filterOutAllExcept(uriInfo.getQueryParameters().getFirst("fields")));
jsonProvider.locateMapper(aClass, mediaType).writer(filters).writeValue(outputStream, object);
}
}
It seems to work, but I am unsure if it is smart to do it like this. I am new to the Jersey library.
My current solution to a similar problem is to register the following servlet filter:
@Singleton
public class ViewFilter implements Filter {
private @Inject Provider<ViewBeanPropertyFilter> filter;
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ObjectWriterInjector.set(new ObjectWriterModifier() {
@Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders, Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
return w.with(new FilterProvider() {
@Override
public BeanPropertyFilter findFilter(Object filterId) {
if(filterId.equals(ViewFilterJacksonModule.FILTER_NAME)) {
return filter.get();
}
return null;
}
});
}
});
chain.doFilter(request, response);
}
public static class ViewBeanPropertyFilter extends SimpleBeanPropertyFilter {
private @Inject ViewManager manager;
@Override
protected boolean include(BeanPropertyWriter writer) {
Class<?> cls = writer.getMember().getDeclaringClass();
return manager.isFieldInView(cls, writer.getMember().getName());
}
@Override
protected boolean include(PropertyWriter writer) {
return true;
}
}
}
It's a little more gnarly than the solution you provided, but leaves the standard JacksonJsonProvider
serialization in place.
It can be improved by pulling the (possibly) existing FilterProvider
via m.getConfig().getFilterProvider()
and delegating to it before / after your filter.
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