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

179,682

Solution 1

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}

Solution 2

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

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

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.

Share:
179,682
Eran Medan
Author by

Eran Medan

CTO at arnica.io (I'm hiring!)

Updated on July 05, 2022

Comments

  • Eran Medan
    Eran Medan almost 2 years

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

  • Bozho
    Bozho about 13 years
    or use <mvc:annotation-driven> I think.
  • Eran Medan
    Eran Medan about 13 years
    I already have serialization OUT (ResponseBody, my problem is serialization IN (RequestBody)
  • Eran Medan
    Eran Medan about 13 years
    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
    Eran Medan about 13 years
    MappingJacksonHttpMessageConverter is already registered, but it works for me only for ResponseBody, how do I make it work for RequestBody?
  • Eran Medan
    Eran Medan about 13 years
    @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...
  • danny.lesnik
    danny.lesnik about 13 years
    @Eran, Have you tried log4j, you will see serialization exception or error coming from controller.
  • Eran Medan
    Eran Medan about 13 years
    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
    Eran Medan about 13 years
    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
    Eran Medan about 13 years
    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
  • Sean Patrick Floyd
    Sean Patrick Floyd about 13 years
    @Eran I know you have solved your problem in the meantime, but I have now added a working example to my answer
  • kamaci
    kamaci over 12 years
    @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?
  • Eran Medan
    Eran Medan over 12 years
    @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.
  • Stuart Blair
    Stuart Blair over 12 years
    @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
  • jackyesind
    jackyesind almost 11 years
    @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? stackoverflow.com/questions/18102452/…
  • egervari
    egervari over 10 years
    This will work because you're doing a POST and original poster was using GET.
  • Archit
    Archit over 8 years
    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.
  • Sean Patrick Floyd
    Sean Patrick Floyd over 8 years
    @Archit please ask a separate question and post your code
  • Dipanshu Verma
    Dipanshu Verma almost 7 years
    Dude thanks, for this brilliant piece of advice, Totally Loved it
  • user3198603
    user3198603 over 6 years
    @SeanPatrickFloyd can you please have a look at stackoverflow.com/questions/46632680/… ?