Spring MVC: using Jackson for incoming requests and FlexJSON Serializer for responses - json

I'd like to use Jackson to deserialize JSON strings from client requests to Java objects and use FlexJson to serialize Java objects to response.
In the nutshell the issue is: how to setup the Spring to use Jackson ONLY for request handling and not for response?
In servlet-context.xml I have:
<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:ref bean="jsonMessageConverter"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</beans:bean>
And in the controller:
#RequestMapping(value = "settings")
public #ResponseBody String getSomeData(#RequestBody UserData userData) {
// userData is automatically deserialized by Jackson
MyView viewForClient = new MyView(userData);
return new JSONSerializer().include(MyView.SERILIZABLE_FIELDS).exclude("*", "*.class").serialize(viewForClient); // here I don't want Jackson to handle the response
}
But this way Jackson also converts to JSON the response already converted by FlexJSON that I don't want.
Is there any solution? Thanks.

You should be able to build a custom MappingJackson2HttpMessageConverter bean where you plug in Jackson for the serialization methods, and FlexJSON for the deserialization methods.

Related

Spring Boot Microservice Jackson JSON serialisation NON NULL

I'm currently working on a Spring Boot ( Version 1.3.1 ) Microservice which connects to MongoDB backend and provides the backend data ( Ex: Provider object ) to the client via controller.
The project has got one class file which extends ResourceSupport ( Ex: ProviderResourceSupport ) and also another class which extends ResourceSupportAssembler class ( Ex: ProviderAssembler ) for generating Links to the Response objects.
Ideally my requirement is to customise the JSON objects on a need basis and as such using #JsonView ( followed this link - https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring ) and added Spring Jackson dependencies in the maven project.
I have also added spring.jackson.serialization-inclusion=non-null & spring.jackson.serialization.indent_output=true in the application.properties.
For one of the method in the controller, the response will be 'ResponseEntity< List< ProviderResourceSupport>>' , and this method is returning with a 'null' response if the data is not present.
I have added #JsonInclude(Include=NON_NULL) on my entity objects and controllers but still getting the 'null' response.
I don't want the 'null' as the response and request you to help me incase if anyone has faced the similar issue.
I fixed this null properties escaping from json response extending a Jackson Mapper Bean but I don't use Spring Boot, take a quickly look and check if this is suitable for you
public class Jackson2ObjectMapperCustom extends Jackson2ObjectMapperFactoryBean {
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
getObject().setSerializationInclusion(JsonInclude.Include.NON_NULL).setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
Hibernate5Module hibernateModule = new Hibernate5Module();
hibernateModule.disable(Feature.USE_TRANSIENT_ANNOTATION);
hibernateModule.enable(Feature.FORCE_LAZY_LOADING);
getObject().registerModules(new JavaTimeModule(), hibernateModule);
getObject().configure(SerializationFeature.INDENT_OUTPUT, true);
getObject().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
getObject().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
}
}
And in my case I use Spring Xml configuration
<bean id="objectMapper" class="com.xxx.common.Jackson2ObjectMapperCustom" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

Deserializing single-attribute JSON payload in Spring MVC controller

I want to create controller methods that semantically look like the following
public HttpEntity<?> deleteUser(String userId){
...
}
The client is going to pass the user ID as part of the JSON payload. If I try to annotate #RequestBody the string parameter and issue a {"userId":"foo"} payload, then I get an exception
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: java.io.PushbackInputStream#7311a203; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:854) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3702) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2798) ~[jackson-databind-2.6.1.jar:2.6.1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:221) ~[spring-web-4.2.0.RELEASE.jar:4.2.0.RELEASE]
And that is reasonable because JSON wants to deserialize a complex object (with namely one attribute) into a String.
I also know that "foo" is not valid JSON. And I know that I can use a Map<String,Object> or even better a ModelMap, and as a last resort I could use query string and #RequestParam, but today I have been clearly asked by my boss to find a way to use a plain string instead of an object, in order for code to look more readable.
How can I force Jackson/MVC to deserialize only the "username" property into a plain old String?
You will usually see this type of error when Spring MVC finds a request mapping that matches the URL path but the parameters (or headers or something) don't match what the handler method is expecting.
If you use the #RequestBody annotation then Spring MVC is expecting to map the entire body of the POST request to an Object,it dont work with String by default.
There are different way to do this as listed below:
1) Change method type of deleteUser() method type to GET instead of Post and use userId as String.
2) You could simply inject the HttpServletRequest into your method and read the body:
public void deleteUser(HttpServletRequest request) {
String userID = IOUtils.toString( request.getInputStream());
// do stuff
}
3) Use a wrapper (java model of the JSON object) that could replace the String parameter,and also this will work fine with the json coming in your post.
public class UserWrapper {
private String userId;
//getter setters
and then use in your controller as:
public void deleteUser(#RequestBody UserWrapper user) {
//do your stuff
}
4) Spring provides a way to configure multiple message converters as shown below:
Note: Then, requests to the various methods must specify the "content-type" header with an appropriate value. For those methods where the request body is mapped to a JAXB bean, specify "application/xml". And for those where the request body is a String, use "text/plain".
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
<ref bean="marshallingConverter" />
<ref bean="stringHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean>
<bean id="marshallingConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxb2Marshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="stringHttpMessageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain"/>
</bean>
Hope this help you!

Spring MVC RESTful web services with Jackson: How to restrict to subset of URLs

Following the steps outlined here http://www.journaldev.com/2552/spring-restful-web-service-example-with-json-jackson-and-client-program , I've been able to setup my RESTful WS exactly how I want it.
Now, I want to make this app context a hybrid of RESTful WS (under a specific URL prefix /myapp/rest/) and standard MVC mappings for the rest of the site.
But from the servlet-context XML file, I can't really make out how to restrict MappingJackson2HttpMessageConverterto only apply to a certain URL prefix.
<!-- Configure to plugin JSON as request and response in method handler -->
<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:ref bean="jsonMessageConverter"/>
</beans:list>
</beans:property>
</beans:bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<beans:bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</beans:bean>
Is there anything else that I should be checking on here?
Thanks
Wong
You can have both SpringMVC style controllers and SpringMVC REST style controllers in the same application. In fact you can write one SpringMVC handler method and a REST Endpoint in same controller too.
#Controller
public class MyController
{
#RequestMapping("/home")
public String homePage(Model model)
{
model.addAttribute("data",someObject);
return "home"; //this will render the view home.jsp
}
#RequestMapping(value="/users", method=RequestMethod.POST)
#ResponseBody
public User saveUser(#RequestBody User user)
{
//persist user
return user; //this will return JSON/XML based representation of User object
}
}
Observe the use of #RequestBody and #ResponseBody annotations for REST style handler methods.

JSON reponse with Spring Controllers in Jetty vs Tomcat

I am building a simple Spring MVC webapp and was developing on jetty. My controller binding used this:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody String getDoc
And returning a String from a JSONObject correctly resolves to JSON in my ajax response.
But using those same controllers, i deployed my gradle war to tomcat and my json came back wrapped as true strings.
So i changed my headers to use Map and that seems to fix things in both jetty and tomcat:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody Map<String, String> getDoc
I convert from the string to a map with this:
HashMap<String, String> jsonResponse = new HashMap<String, String>();
if(claimFolder.has("error")){
response.setStatus(500);
}else{
jsonResponse = new ObjectMapper().readValue(claimFolder.toString(), HashMap.class);
}
return jsonResponse;
My question is why this is nessesary?
Here's my jackson converter configuration:
<bean id="formConverter" class="org.springframework.http.converter.FormHttpMessageConverter" />
<!-- add byte[] converter -->
<bean id="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes" value="application/octet-stream" />
</bean>
<!-- add in our JSON message converter -->
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json;charset=UTF-8" />
</bean>
<!-- add in our plain string message converter -->
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>
<!-- Expose the authenticated handler to all beans that have been declared via annotation -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>
TL;DR: Why does jetty and tomcat return stringified JSON differently?
Well, it's absolutely normal for Spring content negotiation to translate a String object as a simple string without marshalling it to a JSON object. In order to serialize a java String object in JSON object you need to wrap it previously in some java class. For example:
QuestionStatus {
private String status;
public QuestionStatus(String status) {
this.status = status;
}
public getStatus() {
return status;
}
}
Hence you have to return in your Controller method not a String but QuestionStatus.

spring-mvc return raw json string

I want the convenience of automatically serializing objects into JSON and ability to return raw JSON string. I am using Gson instead of Jackson, since Gson has been in my app for a while and I have existing tweaks, converters, and annotations peppered throughout my app.
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
I can automatically serialize pojo's:
#RequestMapping(value="foo/{name}", method = RequestMethod.GET)
public #ResponseBody Shop getShopInJSON(#PathVariable String name) {
return new Shop();
}
I want this to work also:
#RequestMapping(value="rawJsonTest/{name}", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String rawJsonTest(#PathVariable String name) {
return "{\"test\":5}";
}
Result right now is an escaped value:
"{\"test\":5}"
instead of:
{"test":5}
The problem is that your custom converter takes precedence over the default ones. It's thus called, considers the String as a raw String that must be converted to JSON, and thus escapes the double quotes.
I'm not sure if and how it's possible with XML to register a converter after (and not before) the default ones, but you could set register-defaults to false and provide an explicit list of all the converters you want to apply. If org.springframework.http.converter.StringHttpMessageConverter is registered before your custom one, it will be called first and will send the returned String as is.
Thanks for the correct answer, #JB Nizet
Order matters:
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json; charset=UTF-8" />
</bean>
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>