70

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

Eran Medan
  • 44,555
  • 61
  • 184
  • 276
  • I rephrased the question better here: http://stackoverflow.com/questions/5930894/can-jackson-be-used-with-spring-mvc-3-0-to-also-bind-the-requestbody-to-a-java-be (I'll close this one as it seems to be too long and unclear) – Eran Medan May 08 '11 at 23:02
  • It looks like it's not getting called because you're doing a GET but your method is a POST. – egervari Sep 29 '13 at 18:24

5 Answers5

100

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)

See:


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 to do this.

Here's what the response looks like:

{"addition":20,"subtraction":6,"multiplication":91}
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 9
    or use `` I think. – Bozho May 06 '11 at 08:34
  • I already have serialization OUT (ResponseBody, my problem is serialization IN (RequestBody) – Eran Medan May 08 '11 at 21:56
  • MappingJacksonHttpMessageConverter is already registered, but it works for me only for ResponseBody, how do I make it work for RequestBody? – Eran Medan May 08 '11 at 22:23
  • @Danny, Thanks for the update, this is exactly what I have, and I used "application/json" as the content type, could it be that only very simple Beans can be used? e.g. only primitive types? maybe that's what I'm missing... – Eran Medan May 09 '11 at 01:28
  • 1
    @Eran, Have you tried log4j, you will see serialization exception or error coming from controller. – danny.lesnik May 09 '11 at 20:52
  • 1
    No exception in log4j, I'm close to giving up... is there a working sample out there I can start from? (a simple Response/Request Body + JSON / JQuery test?) – Eran Medan May 11 '11 at 02:20
  • 2
    @Eran I know you have solved your problem in the meantime, but I have now added a working example to my answer – Sean Patrick Floyd May 11 '11 at 09:39
  • 1
    @Eran Medan, you say you changed configuration of xml file and it worked however this answer doesn't include anything like that. Which one to follow? – kamaci Sep 20 '11 at 09:13
  • @kamaci - see my update - the change that made it work was so small it's hardly noticable, you can see the working solution both in this answer (on which we are commenting) or in my update. – Eran Medan Sep 22 '11 at 16:14
  • 3
    @SeanPatrickFloyd Wonderful.That's really clear. That said, I noticed a slight problem which surfaced when I tried this. The post body in the example is quoted as { "left": 13 ; "right" : 7 } which isn't legal JSON. I was getting 400 bad request codes when I tried it. Then I noticed that there was a semi-colon in there which isn't part of the JSON syntax. I found success by posting the following through curl - `curl -v -H "Content-Type: application/json" -X POST -d "{\"left\":13,\"right\":7}" http://localhost:8080/test/math` – Stuart Blair Feb 14 '12 at 18:11
  • @SeanPatrickFloyd if i don't have pojo means what should i do for example `math(@RequestBody final Request request)` you have Request java but i don't have class for json at that situation what i do? http://stackoverflow.com/questions/18102452/spring-mvc-415-unsupported-media-type – jackyesind Aug 07 '13 at 16:55
  • Hello Guys please suggest the solution for the same. I have tried but not able to solve. The same configuration I did for 2 other projects it worked but for the current project it is not working. – Archit Dec 04 '15 at 06:55
  • @Archit please ask a separate question and post your code – Sean Patrick Floyd Dec 04 '15 at 09:13
  • @SeanPatrickFloyd can you please have a look at https://stackoverflow.com/questions/46632680/spring-mvc-requestbody-not-working-with-jquery-ajax ? – user3198603 Oct 08 '17 at 15:16
12

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.

Prags
  • 2,457
  • 2
  • 21
  • 38
danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
  • Have if of course, not the question again, I do have ResponseBody working, it's the RequestBody that doesn't work and I'm sure I'm missing something basic... – Eran Medan May 08 '11 at 22:22
  • No additional erros in log4j :( and I haev the same been configuration... Is there a working sample somewhere I can download and debug the differences? – Eran Medan May 11 '11 at 02:18
  • I used your config and replaced the almost identical one I had, I really don't know what small change did the trick, but it did... so THANKS – Eran Medan May 11 '11 at 02:35
9

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');
    }
});
5

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.

matsev
  • 32,104
  • 16
  • 121
  • 156
0

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.

Community
  • 1
  • 1
AmirHd
  • 10,308
  • 11
  • 41
  • 60