How to filter the json response returning from spring rest web service.
When use call the customEvents i only need to outout the eventId and the Event name only. Whe
I was having similar requirement for my existing project. It was a pain to introduce views as a single object is used by multiple controllers. It was not a clean solution to implement filter either. So I have decided to clear those values from my DTO in Controller before sending it to the client. So my own couple of methods (which may take bit of more execution time) to address the issue.
public static void includeFields(Object object, String... includeFields) {
for (PropertyDescriptor propertyDescriptor : PropertyUtils.getPropertyDescriptors(object)) {
if (!Arrays.asList(includeFields).contains(propertyDescriptor.getName())) {
clearValues(object, propertyDescriptor);
}
}
}
public static void excludeFields(Object object, String... includeFields) {
for (PropertyDescriptor propertyDescriptor : PropertyUtils.getPropertyDescriptors(object)) {
if (Arrays.asList(includeFields).contains(propertyDescriptor.getName())) {
clearValues(object, propertyDescriptor);
}
}
}
private static void clearValues(Object object, PropertyDescriptor propertyDescriptor) {
try {
if(propertyDescriptor.getPropertyType().equals(boolean.class)) {
PropertyUtils.setProperty(object, propertyDescriptor.getName(), false);
} else {
PropertyUtils.setProperty(object, propertyDescriptor.getName(), null);
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
//TODO
e.printStackTrace();
}
}
Drawback is the boolean
fields that will always have value and hence will be present in the payload. At least this helped me give dynamic solution and reduce many fields in the payload.
You can use @JsonFilter to archive.
Pojo:
@JsonFilter("myFilter")
public class User {
....
}
Controller:
public String getUser(
@RequestParam(value="id") String id,
@RequestParam(value="requiredFields",required=false ) String requiredFields
) throws JsonParseException, JsonMappingException, IOException {
//Get User
User user = userService.getUser(id);
//Start
ObjectMapper mapper = new ObjectMapper();
// and then serialize using that filter provider:
String json="";
try {
if (requiredFields!= null) {
String[] fields = requiredFields.split("\\,");
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept(new HashSet<String>(Arrays
.asList(fields))));
json = mapper.filteredWriter(filters).writeValueAsString(user);//Deprecated
} else {
SimpleFilterProvider fp = new SimpleFilterProvider().setFailOnUnknownId(false);
mapper.setFilters(fp);
json =mapper.writeValueAsString(user);
}
} catch (JsonGenerationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return json;
}
Get URL: .....&requiredFields=id,Name,Age
I can think of two solutions off hand which both take advantage of Jackson's mixin feature.
The first solution which is by far more complicated but is an awesome approach if what you are describing will be replicated in other parts of the code is described in this link. What happens is that you define an aspect that applies the specific mixin (in your case CustomEventMixin) that you have set in the JsonFilter annotation.
The second solution is far simpler and includes using the jackson object mapper on your own (instead of delegating this responsibility to String) like the following code
@Controller
public class EventController {
private ObjectMapper objectMapper = new ObjectMapper();
public EventController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.addMixInAnnotations(CustomEvent.class, CustomEventMixin.class);
}
@RequestMapping("/customEvents")
@ResponseBody
public String suggest() {
return objectMapper.writeValueAsString(getCustomEvents(), new TypeReference<List<CustomEvent>>() {});
}
}
In both cases you need to define the CustomEventMixin according to Jackson rules
UPDATE:
An example Mixin class would be (say you want to ignore id)
public interface CustomEventMixin {
String name;
@JsonIgnore
String id;
}
You can use @JsonView annotation for this purpose. But unfortunately it works only for 4.x and above.
This is the cleanest way that I know:
public class View {
public interface Summary {}
public interface Details extends Summary{}
}
Class CustomEvent{
@JsonView(View.Summary.class)
long id;
@JsonView(View.Summary.class)
String eventName;
@JsonView(View.Details.class)
Account createdBy;
@JsonView(View.Details.class)
Account modifiedBy;
}
@Controller
public class CustomEventService
{
@JsonView(View.Summary.class)
@RequestMapping("/customEvents")
public @ResponseBody List<CustomEvent> getCustomEventSummaries() {}
@RequestMapping("/customEvents/{eventId}")
public @ResponseBody CustomEvent getCustomEvent(@PathVariable("eventId") Long eventId) {}
}
Keep in mind that by default fields that do not have the @JsonView annotation are also serialized. That's why you need to annotate them all.
For more information please read: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring