I have a class pojo used to return a response to an API call in the rest controller
EmployeeResponse response = validationService.validate(request);
return new ResponseEntity<>(response, HttpStatus.OK);
However now we want to feature flag the controller class so that if a configuration property is not set, the response will not include a property. How can we do that?
public class EmployeeResponse {
private String firstName;
private String lastName
private String address; // don't want to include this if boolean flag is not set
}
EDIT: adding the controller code here to show that an object is returned without being serialized so I don't see how to fit objectMapper into that
@RestController
public class EmployeeController {
@PostMapping(value = "/validate", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EmployeeResponse> get(final @RequestBody EmployeeRequest employeeRequest) {
MasterSubResponse response = validationService.validate(employeeRequest);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
CodePudding user response:
You can use Jackson Filter to control the serialization process. When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests. The idea is to create custom filter where you will place business logic for conditionally rendering desired field from DTO. Then you should return custom object mapper bean which will use filter mentioned above.
To summarize,here are the steps youn need to follow :
- Anottate your DTO class with
@JsonFilter("myFilter")
- Create implementation class for your custom filter
- Create configuration class for
ObjectMapper
where you will set filter created in step 1. - Create your boolean flag in
application.properties
file
Step 1:
import com.fasterxml.jackson.annotation.JsonFilter;
@JsonFilter("myFilter")
public class EmployeeResponse {
private String firstName;
private String lastName;
private String address;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Step 2:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
public class CustomFilter extends SimpleBeanPropertyFilter implements PropertyFilter {
private boolean isSerializable;
@Override
public void serializeAsField
(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (include(writer)) {
if (!writer.getName().equals("address")) {
writer.serializeAsField(pojo, jgen, provider);
return;
}
System.out.println(isSerializable);
if (isSerializable) {
writer.serializeAsField(pojo, jgen, provider);
}
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
@Override
protected boolean include(BeanPropertyWriter writer) {
return true;
}
@Override
protected boolean include(PropertyWriter writer) {
return true;
}
public boolean isSerializable() {
return isSerializable;
}
public void setSerializable(boolean serializable) {
isSerializable = serializable;
}
}
Step 3:
import com.example.demo.filter.CustomFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ObjectMapperCofiguration {
@Value("${isSerializable}")
public boolean isSerializable;
@Configuration
public class FilterConfiguration {
public FilterConfiguration(ObjectMapper objectMapper) {
SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider().setFailOnUnknownId(true);
CustomFilter customFilter = new CustomFilter();
customFilter.setSerializable(isSerializable);
simpleFilterProvider.addFilter("myFilter", customFilter);
objectMapper.setFilterProvider(simpleFilterProvider);
}
}
}
Step 4 : In application.properties file add following property :
isSerializable= false
Step 5:
Create Controller class to test it:
@RestController
public class RestSpringBootController {
@GetMapping(path = "/test")
public ResponseEntity<EmployeeResponse> test() throws JsonProcessingException {
EmployeeResponse employeeResponse = new EmployeeResponse();
employeeResponse.setAddress("addres");
employeeResponse.setFirstName("first");
employeeResponse.setLastName("last");
ResponseEntity<EmployeeResponse> responseEntity = ResponseEntity.ok(employeeResponse);
return responseEntity;
}
}
Finally, when you start your SpringBoot app, with boolean flag isSerializable
set to false
you should get following response:
If you set isSerializable
flag to true
and restart the app, you shoud see following response: