Excluding null fields in pojo response - java

I want to exclude null fields from a pojo
****TransactionHistoryBO Pojo**
package main.java.com.as.model;
import com.fasterxml.jackson.annotation.JsonInclude;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class TransactionHistoryBO
{
private String processId;
private String dateTime;
private Integer status;
private Double pointsEarned;
private String productName;
private String receiptNumber;
public String getProcessId() {
return processId;
}
public void setProcessId(String processId) {
this.processId = processId;
}
public String getDateTime() {
return dateTime;
}
public void setDateTime(String dateTime) {
this.dateTime = dateTime;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Double getPointsEarned() {
return pointsEarned;
}
public void setPointsEarned(Double pointsEarned) {
this.pointsEarned = pointsEarned;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getReceiptNumber() {
return receiptNumber;
}
public void setReceiptNumber(String receiptNumber) {
this.receiptNumber = receiptNumber;
}
}
**
Transaction History Response pojo
public class TransactionHistoryResponse
{
private ArrayList<TransactionHistoryBO> transactions;
#JsonInclude(JsonInclude.Include.NON_NULL)
public ArrayList<TransactionHistoryBO> getTransactions() {
return transactions;
}
#JsonInclude(Include.NON_NULL)
public void setTransactions(ArrayList<TransactionHistoryBO> transactions) {
this.transactions = transactions;
}
}
Array list of type Transaction History BO is used in Transaction History Response pojo.This is the exact pojo that i am showing in response.I would like to exclude the fields with null values in Transaction History BO.
I tried with #JsonInclude(JsonInclude.Include.NON_NULL).It is not working..
Also tried with JsonSerialize,but it is deprecated.Jackson version used is 2.2.2.
Any help would be appreciated..please help..

#JsonInclude(JsonInclude.Include.NON_NULL)
public class TransactionHistoryBO { ... }
#JsonInclude(JsonInclude.Include.NON_NULL)
public class TransactionHistoryResponse { ... }
public class App {
public static void main(String... args) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
TransactionHistoryResponse thr = new TransactionHistoryResponse();
TransactionHistoryBO thbo = new TransactionHistoryBO();
thbo.setProductName("TEST");
thr.setTransactions(new ArrayList<TransactionHistoryBO>());
thr.getTransactions().add(thbo);
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(thr));
}
}
Produces output :
{
"transactions" : [ {
"productName" : "TEST"
} ]
}
No other annotation is used. Just add #JsonInclude annotation to classes not properties.
UPDATE:
Add a custom JacksonJsonProvider to your application
#Provider
public class CustomJsonProvider extends ResteasyJackson2Provider {
#Override
public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
ObjectMapper mapper = locateMapper(type, mediaType);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream);
}
}
Register this provider in your web.xml
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>com.package.CustomJsonProvider</param-value>
</context-param>
Tested with and without this and it works.

Related

How to parse multiple types using jackson #JsonSerialize annotation?

I use #JsonSerialize to convert the enum class to Integer, and the writing is successful; but each enum class must write a converted class, is there a way to write only one conversion class?
I tried to use generics to get the type of the enum class, but failed, this is not allowed
// error code
#JsonSerialize(using = StatusSerializer<StatusEnum>.class)
private Integer status;
#Data
public class ZkUser {
/**
* name
*/
private String name;
/**
* status
*/
#JsonSerialize(using = StatusSerializer.class)
private Integer status;
}
//==========================================================================================
public enum StatusEnum {
// d
ON(1),
OFF(0);
private final Integer code;
public static StatusEnum getEnumByCode(Integer code) {
for (StatusEnum s : values()) {
if (s.code.equals(code)) {
return s;
}
}
return null;
}
StatusEnum(Integer code) {
this.code = code;
}
public Integer getCode() {
return code;
}
}
//=========================================================================================
public class StatusSerializer<T> extends JsonSerializer<Integer> {
private T t;
#Override
public void serialize(Integer value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
var b = StatusEnum.getEnumByCode(value);
jsonGenerator.writeObject(b);
}
}
You can both serialize and deserialize an enum by adding the #JsonValue annotation (see this answer). The following example is based on your enum:
public class Main {
public static enum StatusEnum {
ON(1),
OFF(0);
private final Integer code;
StatusEnum(Integer code) {
this.code = code;
}
#JsonValue
public Integer getCode() {
return code;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(StatusEnum.ON);
System.out.println(jsonString);
StatusEnum readEnum = objectMapper.readValue(jsonString, StatusEnum.class);
System.out.println(readEnum);
}
}
The program outputs:
1
ON

How to parse json file to java using boon and rest?

I'm trying to parse a JSON file which I get via API to pojo. After searching on internet I see boon is working with rest but I can't figure out how.
According to this article it should work but....
In my code HTTP.getJSON() method require a map as parameter which I can't figure out what exactly this map is.
Any genius one can give a working example of boon?
public class ViewTimeline{
public void view() {
ObjectMapper mapper = JsonFactory.create();
List<String> read = IO.readLines("https://corona-api.com/timeline");
Map<String, ?> headers = null ;
List<Timeline> timelineList = mapper.readValue(HTTP.getJSON("https://corona-api.com/timeline", headers), List.class, Timeline.class);
}
}
TimeLine.java
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"updated_at",
"date",
"deaths",
"confirmed",
"recovered",
"active",
"new_confirmed",
"new_recovered",
"new_deaths",
"is_in_progress"
})
public class Timeline {
#JsonProperty("updated_at")
private String updatedAt;
#JsonProperty("date")
private String date;
#JsonProperty("deaths")
private Integer deaths;
#JsonProperty("confirmed")
private Integer confirmed;
#JsonProperty("recovered")
private Integer recovered;
#JsonProperty("active")
private Integer active;
#JsonProperty("new_confirmed")
private Integer newConfirmed;
#JsonProperty("new_recovered")
private Integer newRecovered;
#JsonProperty("new_deaths")
private Integer newDeaths;
#JsonProperty("is_in_progress")
private Boolean isInProgress;
#JsonProperty("updated_at")
public String getUpdatedAt() {
return updatedAt;
}
#JsonProperty("updated_at")
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
#JsonProperty("date")
public String getDate() {
return date;
}
#JsonProperty("date")
public void setDate(String date) {
this.date = date;
}
#JsonProperty("deaths")
public Integer getDeaths() {
return deaths;
}
#JsonProperty("deaths")
public void setDeaths(Integer deaths) {
this.deaths = deaths;
}
#JsonProperty("confirmed")
public Integer getConfirmed() {
return confirmed;
}
#JsonProperty("confirmed")
public void setConfirmed(Integer confirmed) {
this.confirmed = confirmed;
}
#JsonProperty("recovered")
public Integer getRecovered() {
return recovered;
}
#JsonProperty("recovered")
public void setRecovered(Integer recovered) {
this.recovered = recovered;
}
#JsonProperty("active")
public Integer getActive() {
return active;
}
#JsonProperty("active")
public void setActive(Integer active) {
this.active = active;
}
#JsonProperty("new_confirmed")
public Integer getNewConfirmed() {
return newConfirmed;
}
#JsonProperty("new_confirmed")
public void setNewConfirmed(Integer newConfirmed) {
this.newConfirmed = newConfirmed;
}
#JsonProperty("new_recovered")
public Integer getNewRecovered() {
return newRecovered;
}
#JsonProperty("new_recovered")
public void setNewRecovered(Integer newRecovered) {
this.newRecovered = newRecovered;
}
#JsonProperty("new_deaths")
public Integer getNewDeaths() {
return newDeaths;
}
#JsonProperty("new_deaths")
public void setNewDeaths(Integer newDeaths) {
this.newDeaths = newDeaths;
}
#JsonProperty("is_in_progress")
public Boolean getIsInProgress() {
return isInProgress;
}
#JsonProperty("is_in_progress")
public void setIsInProgress(Boolean isInProgress) {
this.isInProgress = isInProgress;
}
}
To parse an json to an object, I used Jackson. I also saw you used Jackson at mapping in Timeline.
Jackson Core: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.11.0
Jackson Databind: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.11.0
Jackson Annotation: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations/2.11.0
This is the way I handled it:
public static void main(String[] args) throws JsonProcessingException {
//my method to read content from website.
//using apache http
String jsonApi = getApi();
ObjectMapper objectMapper = new ObjectMapper();
//todo JsonProcessingException
JsonNode data = objectMapper.readTree(jsonApi);
//get data field from data, which is an array
//todo This can throws error if data field is missing
JsonNode dataArray = data.get("data");
List<Timeline> timelineList = new ArrayList<>();
if(dataArray.isArray()){
for(JsonNode line : dataArray){
//todo this can throws errors. need to handle it.
Timeline timeline = objectMapper.readValue(line.toString(), Timeline.class);
timelineList.add(timeline);
}
}else{
System.out.println("JsonApi is not array: '" + jsonApi + "'");
}
System.out.println("Size: " + timelineList.size());
for(Timeline timeline : timelineList){
System.out.println(timeline.getConfirmed());
}
}
At this code you should handle the exceptions. I marked them by comments.

How to use #JsonSubTypes for polymorphic type handling with Jackson YAML Mapper

I discovered the #JsonTypeInfo annotation via https://www.baeldung.com/jackson-annotations and tried to use this in order to dynamically parse a given YAML-file into different POJOs. The YAML could look like this:
AWSTemplateFormatVersion: '2010-09-09'
Resources:
AWSSNSTopic:
Properties:
Subscription:
Endpoint: someEnpointInfo
Protocol: email
ResourceName: HelloWorldTopic
Type: AWS::SNS::Topic
AWSServerlessFunction:
Properties:
Attributes: SomeString
ResourceName: HelloWorldFunction
Type: AWS::Serverless::Function
Transform: AWS::Serverless-2016-10-31
What I want to do is to parse the Resources dynamically since the properties of the objects inside Resources are dependent of the property Type but cannot be implicated by the keys (such as AWSSNSTopic or AWSServerlessFunction which only happen to be consistent with the type in this case but do not have to).
So I tried to solve this somehow via #JsonSubTypes to track down the Type property and decide what type of POJO this will be mapped on. This could look like follows:
public class AWSLambdaResource {
#JsonProperty("AWSTemplateFormatVersion")
private String AWSTemplateFormatVersion;
#JsonProperty("Transform")
private String Transform;
#JsonProperty("Resources")
private Map<String, Ressource> Resources;
public String getAWSTemplateFormatVersion() {
return AWSTemplateFormatVersion;
}
public void setAWSTemplateFormatVersion(String AWSTemplateFormatVersion) {
this.AWSTemplateFormatVersion = AWSTemplateFormatVersion;
}
public String getTransform() {
return Transform;
}
public void setTransform(String Transform) {
this.Transform = Transform;
}
public Map<String, Ressource> getResources() {
return Resources;
}
public void setResources(Map<String, Ressource> resources) {
Resources = resources;
}
}
public class Ressource {
public ResourceInstance resourceInstance;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "Type")
#JsonSubTypes({
#JsonSubTypes.Type(value = AWSServerlessFunction.class, name = "AWS::Serverless::Function"),
#JsonSubTypes.Type(value = AWSSNSTopic.class, name = "AWS::SNS::Topic")
})
public static class ResourceInstance {
#JsonProperty("Type")
public String type;
#JsonProperty("ResourceName")
public String resourceName;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
}
#JsonTypeName("AWSServerlessFunction")
public static class AWSServerlessFunction extends ResourceInstance {
#JsonProperty("ResourceName")
private String resourceName;
#JsonProperty("Properties")
private Properties properties;
public static class Properties {
#JsonProperty("Attributes")
public String attributes;
public String getAttributes() {
return attributes;
}
public void setAttributes(String attributes) {
this.attributes = attributes;
}
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
#JsonTypeName("AWSSNSTopic")
public static class AWSSNSTopic extends ResourceInstance {
#JsonProperty("Subscription")
public Subscription subscription;
public static class Subscription {
#JsonProperty("Endpoint")
public String endpoint;
#JsonProperty("Protocol")
public String protocol;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
}
public Subscription getSubscription() {
return subscription;
}
public void setSubscription(Subscription subscription) {
this.subscription = subscription;
}
}
public ResourceInstance getResourceInstance() {
return resourceInstance;
}
public void setResourceInstance(ResourceInstance resourceInstance) {
this.resourceInstance = resourceInstance;
}
}
However, when I look into the map there are entries with keys AWSSNSTopic and AWSServerlessFunction but the corresponding resourceInstance objects are null.
Where did I go wrong? Or is this simply not possible to solve with this type of approach?

How to apply a transformation on all values when deserializing Json with Jackson

I want to parse a JSON document with Jackson and apply some transformation on all nodes. For example, let's say that I want all values to be in uppercase after deserialization.
The actual use case is a bit more complex:
transformation is more complex, the transformer class need to be injected with some configuration, I'd like it to be a configureable instance
transformation has to happen on all properties, I'd like to be able to not add an annotation on each property of each class deserialized.
There are enough configuration options / hooks in Jackson, so I'm fairly sure that this is possible, I just can't find my way around.
The test below shows what I'm trying to achieve:
public class JsonValueFilterTest {
private ObjectMapper mapper;
#Before
public void setupObjectMapper() {
mapper = new ObjectMapper();
// TODO: configure mapper to upper case all values
}
#Test
public void printJson() throws IOException {
Entity myEntity = new Entity("myName");
mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
}
#Test
public void valuesAreUpperCasedWhenLoaded() throws IOException {
Entity myEntity = mapper.readValue("{\"name\":\"myName\"}", Entity.class);
assertThat(myEntity.getName()).isEqualTo("MYNAME"); // fails
}
public static class Entity {
private final String name;
#JsonCreator
public Entity(#JsonProperty("name") String name) { this.name = name; }
public String getName() { return name; }
#Override
public String toString() { return "name='" + name + "'"; }
}
}
You can use converter for that simple case to not implement custom deserializer. I don't know why, but It's not working on the creator constructors, though. So you will have to use non-final fields.
public class JsonValueFilterTest {
private ObjectMapper mapper;
#BeforeTest
public void setupObjectMapper() {
mapper = new ObjectMapper();
}
#Test
public void printJson() throws IOException {
Entity myEntity = new Entity("myName");
mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
}
#Test
public void valuesAreUpperCasedWhenLoaded() throws IOException {
Entity myEntity = mapper.readValue("{\"name\":\"myName\"}", Entity.class);
Assert.assertEquals(myEntity.getName(), "MYNAME"); // fails
}
public static class UpCaseConverter extends StdConverter<String, String> {
public String convert(String value) {
return value==null ? null : value.toUpperCase();
}
}
public static class Entity {
private String name;
public Entity() {}
public Entity(String name) {
this.name = name;
}
#JsonDeserialize(converter = UpCaseConverter.class)
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "name='" + name + "'";
}
}
}
My final solution (thanks to Alban):
configure the ObjectMapper with a custom JsonNodeFactory which transforms all text nodes
deserialize json to JsonNode (this will apply transformation)
convert the JsonNode to my custom class
public class JsonValueFilterTest {
private ObjectMapper mapper;
#Before
public void setupObjectMapper() {
mapper = new ObjectMapper();
mapper.setNodeFactory(new JsonNodeFactory() {
#Override
public TextNode textNode(String text) {
return super.textNode(text.toUpperCase());
}
});
}
#Test
public void printJson() throws IOException {
Entity myEntity = new Entity("myName");
mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
}
#Test
public void valuesAreUpperCasedWhenLoaded() throws IOException {
JsonNode jsonNode = mapper.readTree("{\"name\":\"myName\"}");
Entity myEntity = mapper.treeToValue(jsonNode, Entity.class);
assertThat(myEntity.getName()).isEqualTo("MYNAME");
}
public static class Entity {
private final String name;
#JsonCreator
public Entity(#JsonProperty("name") String name) { this.name = name; }
public String getName() { return name; }
#Override
public String toString() { return "name='" + name + "'"; }
}
}

Select JsonView in the Spring MVC Controller

I'm currently writing a REST api using Jackson (2.4.0-rc3) and spring mvc (4.0.3), and I'm trying to make it secure.
In this way, I try to use JsonView to select the parts of the objects that can be serialized.
I've found the solution (which is not for me) to annotate my Controller method with the view I want. But I'd like to select on the fly the view inside the controller.
Is it possible to extend the ResponseEntity class in order to specify which JsonView I want ?
A little piece of code :
Here is the account class
public class Account {
#JsonProperty(value = "account_id")
private Long accountId;
#JsonProperty(value = "mail_address")
private String mailAddress;
#JsonProperty(value = "password")
private String password;
#JsonProperty(value = "insert_event")
private Date insertEvent;
#JsonProperty(value = "update_event")
private Date updateEvent;
#JsonProperty(value = "delete_event")
private Date deleteEvent;
#JsonView(value = PublicView.class)
public Long getAccountId() {
return accountId;
}
#JsonView(value = PublicView.class)
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
#JsonView(value = OwnerView.class)
public String getMailAddress() {
return mailAddress;
}
#JsonView(value = OwnerView.class)
public void setMailAddress(String mailAddress) {
this.mailAddress = mailAddress;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonView(value = OwnerView.class)
public void setPassword(String password) {
this.password = password;
}
#JsonView(value = AdminView.class)
public Date getInsertEvent() {
return insertEvent;
}
#JsonView(value = AdminView.class)
public void setInsertEvent(Date insertEvent) {
this.insertEvent = insertEvent;
}
#JsonView(value = AdminView.class)
public Date getUpdateEvent() {
return updateEvent;
}
#JsonView(value = AdminView.class)
public void setUpdateEvent(Date updateEvent) {
this.updateEvent = updateEvent;
}
#JsonView(value = AdminView.class)
public Date getDeleteEvent() {
return deleteEvent;
}
#JsonView(value = OwnerView.class)
public void setDeleteEvent(Date deleteEvent) {
this.deleteEvent = deleteEvent;
}
#JsonProperty(value = "name")
public abstract String getName();
}
Here is the account controller
#RestController
#RequestMapping("/account")
public class AccountCtrlImpl implements AccountCtrl {
#Autowired
private AccountSrv accountSrv;
public AccountSrv getAccountSrv() {
return accountSrv;
}
public void setAccountSrv(AccountSrv accountSrv) {
this.accountSrv = accountSrv;
}
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getById(accountId), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/get_by_mail_address/{mail_address}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getByMailAddress(#PathVariable(value = "mail_address") String mailAddress) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getByMailAddress(mailAddress), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/authenticate/{mail_address}/{password}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> authenticate(#PathVariable(value = "mail_address") String mailAddress, #PathVariable(value = "password") String password) {
return new ResponseEntity<Account>(HttpStatus.NOT_IMPLEMENTED);
}
}
I really like the solution presented here to dynamically select a json view inside your controller method.
Basically, you return a MappingJacksonValue which you construct with the value you want to return. After that you call setSerializationView(viewClass) with the proper view class. In my use case, I returned a different view depending on the current user, something like this:
#RequestMapping("/foos")
public MappingJacksonValue getFoo(#AuthenticationPrincipal UserDetails userDetails ) {
MappingJacksonValue value = new MappingJacksonValue( fooService.getAll() );
if( userDetails.isAdminUser() ) {
value.setSerializationView( Views.AdminView.class );
} else {
value.setSerializationView( Views.UserView.class );
}
return value;
}
BTW: If you are using Spring Boot, you can control if properties that have no view associated are serialized or not by setting this in your application.properties:
spring.jackson.mapper.default_view_inclusion=true
I've solved my problem extending ResponseEntity like this :
public class ResponseViewEntity<T> extends ResponseEntity<ContainerViewEntity<T>> {
private Class<? extends BaseView> view;
public ResponseViewEntity(HttpStatus statusCode) {
super(statusCode);
}
public ResponseViewEntity(T body, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, BaseView.class), statusCode);
}
public ResponseViewEntity(T body, Class<? extends BaseView> view, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, view), statusCode);
}
}
and ContainerViewEntity encapsulate the object and the selected view
public class ContainerViewEntity<T> {
private final T object;
private final Class<? extends BaseView> view;
public ContainerViewEntity(T object, Class<? extends BaseView> view) {
this.object = object;
this.view = view;
}
public T getObject() {
return object;
}
public Class<? extends BaseView> getView() {
return view;
}
public boolean hasView() {
return this.getView() != null;
}
}
After that, we have convert only the object with the good view.
public class JsonViewMessageConverter extends MappingJackson2HttpMessageConverter {
#Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (object instanceof ContainerViewEntity && ((ContainerViewEntity) object).hasView()) {
writeView((ContainerViewEntity) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}
protected void writeView(ContainerViewEntity view, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType());
ObjectWriter writer = this.getWriterForView(view.getView());
JsonGenerator jsonGenerator = writer.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writer.writeValue(jsonGenerator, view.getObject());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private ObjectWriter getWriterForView(Class<?> view) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
return mapper.writer().withView(view);
}
}
And to finish, I enable the converter
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="wc.handler.view.JsonViewMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
And that's it, I can select the View in the controller
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseViewEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) throws ServiceException {
return new ResponseViewEntity<Account>(this.getAccountSrv().getById(accountId), PublicView.class, HttpStatus.OK);
}
FYI, Spring 4.1 already supported using #JsonView directly on #ResponseBody and ResponseEntity:
Jackson’s #JsonView is supported directly on #ResponseBody and ResponseEntity controller methods for serializing different amounts of detail for the same POJO (e.g. summary vs. detail page). This is also supported with View-based rendering by adding the serialization view type as a model attribute under a special key.
And in http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-jsonview you can find the much simpler solution:
#RestController
public class UserController {
#RequestMapping(value = "/user", method = RequestMethod.GET)
#JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
#JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
This works great :
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public void getZone(#PathVariable long id, #RequestParam(name = "tree", required = false) boolean withChildren, HttpServletResponse response) throws IOException {
LOGGER.debug("Get a specific zone with id {}", id);
Zone zone = zoneService.findById(id);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (withChildren) {
response.getWriter().append(mapper.writeValueAsString(zone));
} else {
response.getWriter().append(mapper.writerWithView(View.ZoneWithoutChildren.class).writeValueAsString(zone));
}
}

Categories