JQuery, Spring MVC @RequestBody and JSON - making it work together

JavaJqueryJsonSpringSpring Mvc

Java Problem Overview


I would like to have a bidirectional JSON to Java serialization

I'm using successfully the Java to JSON to JQuery path... (@ResponseBody) e.g.

@RequestMapping(value={"/fooBar/{id}"}, method=RequestMethod.GET)
	 public @ResponseBody FooBar getFooBar(
            @PathVariable String id,
            HttpServletResponse response , ModelMap model) {
		response.setContentType("application/json");
...
}

and In JQuery I use

$.getJSON('fooBar/1', function(data) {
    //do something
});

this works well (e.g. annotations work already, thanks to all the answerers)

However, how do I do the reverse path: have JSON be serialized to a Java Object back using RequestBody?

no matter what I try, I can't get something like this to work:

@RequestMapping(value={"/fooBar/save"}, method=RequestMethod.POST)
public String saveFooBar(@RequestBody FooBar fooBar,
        HttpServletResponse response , ModelMap model) {

  //This method is never called. (it does when I remove the RequestBody...)
}

I have Jackson configured correctly (it serializes on the way out) and I have MVC set as annotations driven of course

How do I make it work? is it possible at all? or is Spring / JSON / JQuery is oneway (out)?


Update:

I changed this Jackson setting

<bean id="jsonHttpMessageConverter"
	class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />

<!-- Bind the return value of the Rest service to the ResponseBody. -->
<bean
	class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
	<property name="messageConverters">
		<util:list id="beanList">
			<ref bean="jsonHttpMessageConverter" />
<!-- 			<ref bean="xmlMessageConverter" /> -->				
		</util:list>
	</property>
</bean>

To the (almost similiar one) suggested

<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> 

And it seems to work! I don't know what exactly did the trick, but it works...

Java Solutions


Solution 1 - Java

I'm pretty sure you only have to register MappingJacksonHttpMessageConverter

(the easiest way to do that [is through <mvc:annotation-driven /> in XML or @EnableWebMvc in Java][2])

See:

  • [this forum post][3] and
  • [7.6.5 Configuring Formatting in Spring MVC][4]

Here's a working example:

Maven POM

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion><groupId>test</groupId><artifactId>json</artifactId><packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version><name>json test</name>
	<dependencies>
		<dependency><!-- spring mvc -->
			<groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>3.0.5.RELEASE</version>
		</dependency>
		<dependency><!-- jackson -->
			<groupId>org.codehaus.jackson</groupId><artifactId>jackson-mapper-asl</artifactId><version>1.4.2</version>
		</dependency>
	</dependencies>
	<build><plugins>
			<!-- javac --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version><configuration><source>1.6</source><target>1.6</target></configuration></plugin>
			<!-- jetty --><plugin><groupId>org.mortbay.jetty</groupId><artifactId>jetty-maven-plugin</artifactId>
			<version>7.4.0.v20110414</version></plugin>
	</plugins></build>
</project>

in folder src/main/webapp/WEB-INF

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<servlet><servlet-name>json</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>json</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

json-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd">

	<import resource="classpath:mvc-context.xml" />
	
</beans>

in folder src/main/resources:

mvc-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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">

	<mvc:annotation-driven />
	<context:component-scan base-package="test.json" />
</beans>

In folder src/main/java/test/json

TestController.java

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping(method = RequestMethod.POST, value = "math")
    @ResponseBody
    public Result math(@RequestBody final Request request) {
        final Result result = new Result();
        result.setAddition(request.getLeft() + request.getRight());
        result.setSubtraction(request.getLeft() - request.getRight());
        result.setMultiplication(request.getLeft() * request.getRight());
        return result;
    }

}

Request.java

public class Request implements Serializable {
    private static final long serialVersionUID = 1513207428686438208L;
    private int left;
    private int right;
    public int getLeft() {return left;}
    public void setLeft(int left) {this.left = left;}
    public int getRight() {return right;}
    public void setRight(int right) {this.right = right;}
}

Result.java

public class Result implements Serializable {
    private static final long serialVersionUID = -5054749880960511861L;
    private int addition;
    private int subtraction;
    private int multiplication;

    public int getAddition() { return addition; }
    public void setAddition(int addition) { this.addition = addition; }
    public int getSubtraction() { return subtraction; }
    public void setSubtraction(int subtraction) { this.subtraction = subtraction; }
    public int getMultiplication() { return multiplication; }
    public void setMultiplication(int multiplication) { this.multiplication = multiplication; }
}

You can test this setup by executing mvn jetty:run on the command line, and then sending a POST request:

URL:        http://localhost:8080/test/math
mime type:  application/json
post body:  { "left": 13 , "right" : 7 }

I used the [Poster Firefox plugin][5] to do this.

Here's what the response looks like:

{"addition":20,"subtraction":6,"multiplication":91}

[2]: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-config-enable "Enabling the MVC Java Config or the MVC XML Namespace" [3]: http://forum.springsource.org/archive/index.php/t-89618.html [4]: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#format-configuring-formatting-mvc [5]: https://addons.mozilla.org/en-US/firefox/addon/poster/

Solution 2 - Java

In Addition you also need to be sure that you have

 <context:annotation-config/> 

in your SPring configuration xml.

I also would recommend you to read this blog post. It helped me alot. Spring blog - Ajax Simplifications in Spring 3.0

Update:

just checked my working code where I have @RequestBody working correctly. I also have this bean in my config:

<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>

May be it would be nice to see what Log4j is saying. it usually gives more information and from my experience the @RequestBody will fail if your request's content type is not Application/JSON. You can run Fiddler 2 to test it, or even Mozilla Live HTTP headers plugin can help.

Solution 3 - Java

In addition to the answers here...

if you are using jquery on the client side, this worked for me:

Java:

@RequestMapping(value = "/ajax/search/sync") 
public String sync(@RequestBody Foo json) {

Jquery (you need to include Douglas Crockford's json2.js to have the JSON.stringify function):

$.ajax({
	type: "post",
	url: "sync", //your valid url
	contentType: "application/json", //this is required for spring 3 - ajax to work (at least for me)
	data: JSON.stringify(jsonobject), //json object or array of json objects
	success: function(result) {
		//do nothing
	},
	error: function(){
	    alert('failure');
	}
});

Solution 4 - Java

If you do not want to configure the message converters yourself, you can use either @EnableWebMvc or <mvc:annotation-driven />, add Jackson to the classpath and Spring will give you both JSON, XML (and a few other converters) by default. Additionally, you will get some other commonly used features for conversion, formatting and validation.

Solution 5 - Java

In case you are willing to use Curl for the calls with JSON 2 and Spring 3.2.0 in hand checkout the FAQ here. As AnnotationMethodHandlerAdapter is deprecated and replaced by RequestMappingHandlerAdapter.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEran MedanView Question on Stackoverflow
Solution 1 - JavaSean Patrick FloydView Answer on Stackoverflow
Solution 2 - Javadanny.lesnikView Answer on Stackoverflow
Solution 3 - Javarafael lean ansayView Answer on Stackoverflow
Solution 4 - JavamatsevView Answer on Stackoverflow
Solution 5 - JavaAmirHdView Answer on Stackoverflow