If I use the following configuration then jackson converter works (mvc declaration is last)
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
<context:component-scan base-package="com.base" />
<mvc:annotation-driven />
If I use this configuration in dispatcher.xml then validation works but conversion does not. (mvc declaration first)
<context:component-scan base-package="com.base" />
<mvc:annotation-driven />
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
Any help greatly appreciated.
Spring version 4.0.6
I chose the part where validation is working and added this in the code base.
#RequestMapping(value = "url", method = RequestMethod.GET)
protected void getLocationAsJson(#PathVariable("id") Integer id,
#RequestParam("cid") Integer cid, HttpServletResponse response) {
MappingJacksonHttpMessageConverter jsonConverter =
new MappingJacksonHttpMessageConverter();
Location loc= new Location(id);
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
if (jsonConverter.canWrite(requestedLocation.getClass(), jsonMimeType)) {
try {
jsonConverter.write(requestedLocation, jsonMimeType,
new ServletServerHttpResponse(response));
} catch (IOException m_Ioe) {
// TODO: announce this exception somehow
} catch (HttpMessageNotWritableException p_Nwe) {
// TODO: announce this exception somehow
}
}
}
Now the validation works as well as JSON returning.
The method is not returning anything.
RequestMappingHandlerAdapter's xml configuration is bit complicated. The problem with this configuration is, it removes spring default configuration for converters. It is better to use coding version of this configuration. Spring default configuration will be intact this way. Here is sample configurations.
Suggested solution, posted on numerous blogs. But not working in my case.
https://dzone.com/articles/customizing
http://www.java-allandsundry.com/2014/09/customizing-httpmessageconverters-with.html
#Configuration
public class MessageConvertorConfiguration extends WebMvcConfigurationSupport {
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
Custom360DateFormat dateFormat = new Custom360DateFormat();
dateFormat.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
dateFormat.setDateTimeFormat(new SimpleDateFormat("MM/dd/yyyy hh:mm a"));
objectMapper.setDateFormat(dateFormat);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
}
Working Solution
#Configuration
public class MessageConvertorConfiguration {
private MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
Custom360DateFormat dateFormat = new Custom360DateFormat();
dateFormat.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
dateFormat.setDateTimeFormat(new SimpleDateFormat("MM/dd/yyyy hh:mm a"));
objectMapper.setDateFormat(dateFormat);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Autowired
public void updateJacksonConvertor(RequestMappingHandlerAdapter handlerAdapter) {
//remove default jakson convertor
Iterator<HttpMessageConverter<?>> convertorsIterator = handlerAdapter.getMessageConverters().iterator();
while (convertorsIterator.hasNext()) {
HttpMessageConverter converter = convertorsIterator.next();
if(converter instanceof AbstractJackson2HttpMessageConverter) {
convertorsIterator.remove();
}
}
handlerAdapter.getMessageConverters().add(customJackson2HttpMessageConverter());
}
}
Related
In my spring MVC 4.1.5 application configured to use Thymeleaf 2.1.4 (before it was using JSP and it worked fine), i am unable to return a JSON response.
It always returns a full HTML page weather my request mapping is in a #RestController or if its annotated with #responsebody
Here are the controllers
in an #controller class, i have below mapping
#RequestMapping(value = { "/", "/land", "/login" }, method = RequestMethod.GET)
public String getLogin(Model model, HttpSession session) {
session.setAttribute("login", "none");
System.out.println(programId);
model.addAttribute("signUpForm", new SignUpForm());
return "login";
}
and in #RestController class, below is the post method for same URL
#RequestMapping(value = {"/login" }, method = RequestMethod.POST )
public #ResponseBody HashMap<String, Object> login2(#RequestBody SignUpForm signUpForm, HttpServletRequest request,
HttpServletResponse httpServletResponse, HashMap<String, Object> mo, HttpSession session ) {
User user = userDao.findUserByName(signUpForm.getUserName());
if (user != null && encoder.matches(signUpForm.getPassword(), user.getPassword())&& user.getProgram_id()==3) {/* && user.getProgram_id()==3*/
session.setMaxInactiveInterval(1200);
System.out.println(session.getMaxInactiveInterval()+":"+session.getLastAccessedTime()+":"+session.getCreationTime()+":"+session.getServletContext().getContextPath());
session.setAttribute("login", "success");
mo.put("redirect", "/home");
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
session.setAttribute("urip", ipAddress);
return mo;
} else {
mo.put("error", "Login failed. Please check your credentials");
return mo;
}
}
Below is my xml configuration
<context:component-scan base-package="com.company.cardholder" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:default-servlet-handler />
<mvc:interceptors>
<bean class="com.company.cardholder.session.interceptors.URLInterceptor" />
</mvc:interceptors>
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<!-- Template cache is set to false (default is true). -->
<property name="cacheable" value="false" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
</bean>
Here is my JSON call
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: JSON.stringify({
userName: $form.find('#userName').val(),
password: $form.find('#password').val(),
}),
contentType: "application/json",
/*dataType: 'json',*/
complete: function(data) {
console.log(data);
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
}else if(data.error){
$messageError.text(data.error);
$messageError.removeClass('hidden');
$messageSuccess.addClass('hidden');
}
}
});
Ok. Here is what i figured out to make it work but i still am not sure of the reason.
public #ResponseBody HashMap<String, Object> login2(#RequestBody SignUpForm signUpForm, HttpServletRequest request,
HttpServletResponse httpServletResponse, HashMap<String, Object> mo, HttpSession session ){
////
}
In the above method signature, i was "injecting" a hashmap. And spring framework default or some unknown config decided to inject a "Binding Result aware Hashmap." I am not sure what difference it would have made. But to fix it, i had to do a
HashMap<String, Object> mo=new HashMap<String, Object>;
inside the method body and remove the injected hashmap.
If anyone is able to understand this behaviour, please explain it. I feel i missed something basic in my awareness of spring framework.
I have a Spring MVC Rest service that return JSON value, i have 8 row, here is my JSON
[
{"no":"1","date":"2015-03-30","grandtotal":699618,"diskon":699618},
{"no":"2","date":"2015-03-30","grandtotal":1867949,"diskon":1867949},
{"no":"3","date":"2015-03-27","grandtotal":2190909,"diskon":2190909},
{"no":"4","date":"2015-03-26","grandtotal":8616120,"diskon":8616120},
{"no":"5","date":"2015-03-26","grandtotal":1095455,"diskon":1095455},
{"no":"6","date":"2015-03-24","grandtotal":938961,"diskon":938961},
{"no":"7","date":"2015-03-24","grandtotal":5603848,"diskon":5603848},
{"no":"8","date":"2015-03-20","grandtotal":3735899,"diskon":3735899}
]
What i trying.. Here is my controller. SpringRestController.java
Jackson Way :
#RequestMapping(value = "/view", method = RequestMethod.GET)
public String initCreationForm(Map<String, Object> model) {
String url = "http://localhost:8080/SpringServiceJson/view/";
RestTemplate restTemplate = new RestTemplate();
TotalDiscList totaldisc = restTemplate.getForObject(url, TotalDisc.class);
model.put("DiscValue",totaldisc);
return "salesorders/totalDisc";
}
Gson way :
public String initCreationForm(Map<String, Object> model) {
String url = "http://localhost:8080/SpringServiceJson/view/";
Gson gson = new Gson();
Collection<TotalDisc> totaldisc = gson.fromJson(url, PiutangListJson.class);
model.put("DiscValue",totaldisc);
return "salesorders/totalDisc";
}
what i missed here? it always give me this error
"Could not extract response: no suitable HttpMessageConverter found for response type [class [Lorg.springframework.samples.my.model.TotalDiscList;] and content type [application/json]"
Here is my object TotalDiscList.java
public class TotalDiscList {
private String no;
#DateTimeFormat(pattern="dd-MM-yyyy")
private Date date;
private long grandtotal;
private long diskon;
//getter setter skipped
}
i should return List<TotalDiscList> totaldisc = restTemplate.getForObject(url, List<TotalDisc>.class); right?
how i do that properly?
If you have a servlet-context.xml, you can add the message-convertor there, like below :
<beans:bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters" ref="jsonMessageConverter" />
</beans:bean>
<beans:bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
I am doing the same and it works.
Edit
You need to provide a Message converter to your RestTemplate
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
And then try using an array, such as:
TotalDiscList[] totaldisc = restTemplate
.getForObject(url, TotalDiscList[].class);
you can configure a json message converter:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
and then, you can just annotate your method:
#RequestMapping(value = "/view", method = RequestMethod.POST)
public TotalDiscList[] createDiscList(
#RequestBody TotalDiscList[] object) {
}
which would cause the message converter to try to convert to the given class.
Here is the thing... i'm using jtable (jquery) to show some user data. This component needs a json with two fields: Result and Records. In my controller i have a method to return the json:
#RequestMapping(method=RequestMethod.POST, value="/getUsersInJson")
public #ResponseBody String getUsersInJsonHandler(){
ElementsInList<User> users = new ElementsInList<User>();
users.setItems(userService.getUsers());
return users;
}
The class ElementsInList contains two fields: result and records. Result is a string to get the success message and records is a parametrized list which contains in this case a list of users. I get this JSON:
"{"result":"OK","records":[{"username":"john",
But i need this:
"{"Result":"OK","Records":[{"username":"john",...
This is my Mapping:
<!-- Json converter bean -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
How can i do it? I've checked some posts but have old versions.
I'm using Spring 3, Spring MVC and jQuery.
I solved it by using JsonProperty annotation. You can give the name that jackson will use to build the json field. Here there is an example with jtable (jquery):
public class ElementsInList<T> {
#JsonProperty("Result")
private String result = "OK";
#JsonProperty("Records")
private List<T> records;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public List<T> getRecords() {
return records;
}
public void setRecords(List<T> records) {
this.records = records;
}
}
The result json is this: {"Result":"OK","Records":[{"roleName":"admin"...
But there is more about this annotations. Check the api for more info: http://fasterxml.github.io/jackson-annotations/javadoc/2.1.0/com/fasterxml/jackson/annotation/package-summary.html
I am using Spring REST framework and Jackson JSON
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="customObjectMapper" />
<property name="supportedMediaTypes" value="application/json" />
</bean>
<bean id="customObjectMapper"class="com.rackspace.payment.webapp.utils.JaxbJacksonObjectMapper" />
public class JaxbJacksonObjectMapper extends ObjectMapper {
#SuppressWarnings("deprecation")
public JaxbJacksonObjectMapper() {
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
this.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
super.getDeserializationConfig().withAnnotationIntrospector(introspector);
this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
super.getSerializationConfig().withAnnotationIntrospector(introspector);
}
}
//JXAB element
#XmlAttribute(name = "createDate")
#XmlJavaTypeAdapter(CustomDateAdapter.class)
protected Date createDate;
The issue I face is, when the JSON output is displayed 'createDate' element is displayed like a string the xml adapter class is not invoked, whereas it works fine in XML output.
What am I missing? how to invoke the XmlJavaTypeAdapter in JSON format.
the code below did the trick for me .
ObjectMapper objectMapper = new CustomObjectMapper(ENTITY_WITH_ID_DESERIALIZER);
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary);
objectMapper.setAnnotationIntrospector(pair);
objectMapper.configure(DeserializationConfig.Feature.USE_ANNOTATIONS, true);
I don't like Jackson.
I want to use ajax but with Google Gson.
So I'm trying to figure out how to implement my own HttpMessageConverter to use it with #ResponseBody annotation.
Can someone take a time to show me the way I should go? What configurations should I turn on?
Also I'm wondering if I can do this and still use <mvc:annotation-driven />?
Thanks in advance.
I've already asked it in Spring Community Foruns about 3 days ago with no answer so I'm asking here to see if I get a better chance.
Spring Community Forums link to my question
I've also made an exhaustive search on the web and found something interesting on this subject but it seems they're thinking to put it in Spring 3.1 and I'm still using spring 3.0.5:
Jira's Spring Improvement ask
Well... now I'm trying to debug Spring code to find out myself how to do this, but I'm having some problems like I've said here:
Spring Framework Build Error
If there is another way to do this and I'm missing it, please let me know.
Well... it was so hard to find the answer and I had to follow so many clues to incomplete information that I think it will be good to post the complete answer here. So it will be easier for the next one searching for this.
First I had to implement the custom HttpMessageConverter:
package net.iogui.web.spring.converter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private Gson gson = new Gson();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public GsonHttpMessageConverter(){
super(new MediaType("application", "json", DEFAULT_CHARSET));
}
#Override
protected Object readInternal(Class<? extends Object> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
try{
return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);
}catch(JsonSyntaxException e){
throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
}
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected void writeInternal(Object t,
HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//TODO: adapt this to be able to receive a list of json objects too
String json = gson.toJson(t);
outputMessage.getBody().write(json.getBytes());
}
//TODO: move this to a more appropriated utils class
public String convertStreamToString(InputStream is) throws IOException {
/*
* To convert the InputStream to String we use the Reader.read(char[]
* buffer) method. We iterate until the Reader return -1 which means
* there's no more data to read. We use the StringWriter class to
* produce the string.
*/
if (is != null) {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
return writer.toString();
} else {
return "";
}
}
}
Then I had to strip off the annnotaion-driven tag and configure all by my own hands on the spring-mvc configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Configures the #Controller programming model -->
<!-- To use just with a JSR-303 provider in the classpath
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="net.iogui.web.spring.util.CommonWebBindingInitializer" />
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="net.iogui.web.spring.converter.GsonHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" />
<!-- bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /-->
</list>
</property>
</bean>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<context:component-scan base-package="net.iogui.teste.web.controller"/>
<!-- Forwards requests to the "/" resource to the "login" view -->
<mvc:view-controller path="/" view-name="home"/>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
See that, to make the Formater and Validator to work, we have to build a custom webBindingInitializer too:
package net.iogui.web.spring.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.WebRequest;
public class CommonWebBindingInitializer implements WebBindingInitializer {
#Autowired(required=false)
private Validator validator;
#Autowired
private ConversionService conversionService;
#Override
public void initBinder(WebDataBinder binder, WebRequest request) {
binder.setValidator(validator);
binder.setConversionService(conversionService);
}
}
An Interesting thing to see is that In order to make the configuration work without the annotaion-driven tag, we have to manually configure a AnnotationMethodHandlerAdapter and a DefaultAnnotationHandlerMapping. And in order to make the AnnotationMethodHandlerAdapter capable of handling formatting and validation, we had to configure a validator, a conversionService and to build a custom webBindingInitializer.
I hope all this helps someone else besides me.
On my desperate search, this #Bozho post was extremely util. I am also grateful to #GaryF couse his answer took me to the #Bozho post.
To you that are trying to do this in Spring 3.1, see #Robby Pond answer.. A lot easier, isn't it?
You need to create a GsonMessageConverter that extends AbstractHttpMessageConverter and use the mvc-message-converters tag to register your message converter. That tag will let your converter take precedence over the Jackson one.
If you want to add a message converter without messing with xml here is a simple example
#Autowired
private RequestMappingHandlerAdapter adapter;
#PostConstruct
public void initStuff() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
BufferedImageHttpMessageConverter imageConverter = new BufferedImageHttpMessageConverter();;
messageConverters.add(0,imageConverter);
}
I had situation where usage of Jackson would require me to alter other group's (in the same company) code. Didn't like that. So I chose to use Gson and register TypeAdapters as needed.
Hooked up a converter and wrote a few integration tests using spring-test (used to be spring-mvc-test). No matter what variation I tried (using mvc:annotation-driven OR manual definition of the bean). None of them worked. Any combination of these always used the Jackson Converter which kept on failing.
Answer> Turns out that MockMvcBuilders' standaloneSetup method "hard" coded the message converters to default versions and ignored all my changes above. Here is what worked:
#Autowired
private RequestMappingHandlerAdapter adapter;
public void someOperation() {
StandaloneMockMvcBuilder smmb = MockMvcBuilders.standaloneSetup(controllerToTest);
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
HttpMessageConverter<?> ary[] = new HttpMessageConverter[converters.size()];
smmb.setMessageConverters(conveters.toArray(ary));
mockMvc = smmb.build();
.
.
}
Hope this helps someone, in the end I used annotation-driven and re-purposing android's converter
Notice that GsonHttpMessageConverter was added recently to Spring (4.1)
Robby Pond is basically correct, but note that his suggestion to use the mvc:message-converters tag requires that you use 3.1. Since 3.1 is currently only a milestone release (M1), I'd suggest registering your converter this way after creating it:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="someMessageConverter"/>
<ref bean="someOtherMessageConverter"/>
</util:list>
</property>
</bean>
Or as mentioned in Jira's Spring Improvement ask, write a BeanPostProcessor that adds your HttpMessageConvertor to the AnnotationMethodHandlerAdapter
You can do this by writing the WebConfig file as a Java File. Extend your config file with WebMvcConfigurerAdapter and override extendMessageConverters method to add your intented Message Convertor. This method will retain the default converters added by Spring and will add your convertor at the end. Apparently you have full control with the list and you can add where ever you want in the list.
#Configuration
#EnableWebMvc
#ComponentScan(basePackageClasses={WebConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new GsonHttpMessageConverter());
}
}
package net.iogui.web.spring.converter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private Gson gson = new Gson();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public GsonHttpMessageConverter(){
super(new MediaType("application", "json", DEFAULT_CHARSET));
}
#Override
protected Object readInternal(Class<? extends Object> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
try{
return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);
}catch(JsonSyntaxException e){
throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
}
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected void writeInternal(Object t,
HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//TODO: adapt this to be able to receive a list of json objects too
String json = gson.toJson(t);
outputMessage.getBody().write(json.getBytes());
}
//TODO: move this to a more appropriated utils class
public String convertStreamToString(InputStream is) throws IOException {
/*
* To convert the InputStream to String we use the Reader.read(char[]
* buffer) method. We iterate until the Reader return -1 which means
* there's no more data to read. We use the StringWriter class to
* produce the string.
*/
if (is != null) {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
return writer.toString();
} else {
return "";
}
}