Merging View and Filter in Spring MVC - REST

415 views
Skip to first unread message

bosbeles2006

unread,
Mar 21, 2014, 10:39:31 AM3/21/14
to jackso...@googlegroups.com
****** The STORY begins:****** 

I have modified MappingJackson2HttpMessageConverter as Marty stated at http://martypitt.wordpress.com/2012/11/05/custom-json-views-with-spring-mvc-and-jackson/

This works fine:
@ResponseView(SummaryView.class)
@RequestMapping(value = "deneme", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<Employee> list(HttpSession session) {
return employeeBS.listAllWithDetail();
}

for the entity:
@Entity
@Table(name = "Employee")
public class Employee extends AbstractBaseModel {

@JsonView(SummaryView.class)
@Getter
@Setter
@Column(name = "FirstName")
private String firstName;

@JsonView(SummaryView.class)
@Getter
@Setter
@Column(name = "LastName")
private String lastName;

@JsonView(DetailView.class)
@Getter
@Setter
@Column(name = "Salary")
private int salary;

The key part is:

       private ObjectMapper getMapperForView(final Class<?> view, final String filter, final String expand) {
ObjectMapper mapper = new ObjectMapper() {
private static final long serialVersionUID = -4051244861173830416L;

protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {

SerializationConfig withView = config.withView(view);

return super._serializerProvider(withView);
}

};

mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

return mapper;
}

This works fine, also.

Later, I have read the best practices for a Rest API at http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
I like the following two suggestions: fields and expand
e.g.
GET /tickets?fields=id,subject,customer_name

****** The STORY ends:****** 

I want to implement fields filtering in Spring MVC. I have added filter parameter to the request and I have passed the necessary parameters to this java class:

/**
 * Adds support for Jackson's JsonView on methods annotated with a
 * {@link ResponseView} annotation
 * 
 * @author martypitt
 * 
 */
public class ViewAwareJsonMessageConverter extends MappingJackson2HttpMessageConverter {

public ViewAwareJsonMessageConverter() {
super();
ObjectMapper defaultMapper = new ObjectMapper();

defaultMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
setObjectMapper(defaultMapper);
}

@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
if (object instanceof DataView && ((DataView) object).hasView()) {
writeView((DataView) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}

protected void writeView(DataView view, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
ObjectMapper mapper = getMapperForView(view.getView(), view.getFilter(), view.getExpand());
JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
mapper.writeValue(jsonGenerator, view.getData());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}

private ObjectMapper getMapperForView(final Class<?> view, final String filter, final String expand) {
ObjectMapper mapper = new ObjectMapper() {
private static final long serialVersionUID = -4051244861173830416L;

protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {

System.out.println("View:" + view + " Filter:" + filter + " Expand:" + expand);
SerializationConfig withView = config.withView(view);

if (fields != null && !fields.equals("")) {
SimpleBeanPropertyFilter propertyFilter = SimpleBeanPropertyFilter.filterOutAllExcept(fields
.split(","));
SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider().addFilter("myFilter",
propertyFilter);
withView = withView.withFilters(simpleFilterProvider);
}

return super._serializerProvider(withView);
}

};

mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

return mapper;
}
}

The problem is when I give no filter parameter, now the default behavior with VIEW is no more working. It displays empty.

1. How I can achieve to filter fields after a specific VIEW is applied?

2. Also I wonder, why we have to use @JSonFilter annotation to the Employee class.

3. After completed field filtering part, I want to implement EXPAND part. 
   What do you suggest?




Tatu Saloranta

unread,
Mar 24, 2014, 12:39:08 PM3/24/14
to jackso...@googlegroups.com
Quick note here: if at all possible, you should try to use `ObjectWriter`, and its "withView()" factory method for applying views, instead of trying to make this through `ObjectMapper`.
I realize that frameworks may make it difficult, but for per-call configuration, ObjectReader and ObjectWriter are the abstractions to use: they are reconfigurable in thread-safe manner, and are cheap to create (unlike ObjectMapper that is very costly to create).

So if possible, it'd make sense to use ObjectMapper as the factory for writer instances, and writers can then be reconfigured on per-call basis using various "withXxx()" methods (making sure to use writer that is returned).

-+ Tatu +-



--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages