8

I create the JSON as follows:

    var manager = {
        username: "admin",
        password: "admin"
    };
    var userToSubscribe = {
        username: "newuser",
        password: "newpassword",
        email: "user@1and1.es"
    };

    var openid = "myopenid";

    var subscription = {
            manager: manager,
            userToSubscribe : userToSubscribe,
            openid : openid
    };

    $.ajax({
        url: '/myapp/rest/subscribeUser.json',
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        mimeType: 'application/json',
        data: JSON.stringify({subscription : subscription})   
    });

This is the JSON that is sent:

{"subscription":{"manager":{"username":"admin","password":"admin"},"userToSubscribe":{"username":"newuser","password":"newpassword","email":"user@1and1.es"},"openid":"myopenid"}}  

And I would like to map this JSON to a Wrapper Class. This is the wrapper:

private class Subscription{
    private User manager;
    private User userToSubscribe;
    private String openid;
    public User getManager() {
        return manager;
    }
    public void setManager(User manager) {
        this.manager = manager;
    }
    public User getUserToSubscribe() {
        return userToSubscribe;
    }
    public void setUserToSubscribe(User userToSubscribe) {
        this.userToSubscribe = userToSubscribe;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
}

The jackson dependency in the pom.xml (I'm using spring 3.1.0.RELEASE):

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.10</version>
    </dependency>

The mapping in rest-servlet.xml

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
   <property name="messageConverters">
       <list>
           <ref bean="jsonConverter" />
       </list>
   </property>
</bean>

<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
   <property name="supportedMediaTypes" value="application/json" />
</bean>

And the header of the controller method:

public @ResponseBody SimpleMessage subscribeUser(@RequestBody Subscription subscription)

As a result of the POST I receive a 400 Incorrect request error. Is it possible to do this or do i need to do it with @RequestBody String or @RequestBody Map<String,Object> and decode the JSON myself?

Thanks!

mannuk
  • 1,259
  • 5
  • 21
  • 43
  • There should be nothing preventing you from doing this. Error 400 indicates something either wrong with the request or the mapping. Does the `User` class have a default no-arg constructor. – M. Deinum Oct 31 '13 at 10:40
  • The wrapper is an absolute different concept in Java, what you have a couple of objects that you want to aggregate in the outer class. –  Oct 31 '13 at 11:12
  • what version of spring you are using? – pappu_kutty Oct 31 '13 at 11:52
  • Can you post the mapping for that controller method and the code that is sending the JSON? – Will Keeling Oct 31 '13 at 12:25
  • Give a minute I'm going to update the post – mannuk Oct 31 '13 at 12:43
  • The post is now updated. I included my pom dependencies, spring version and the converters. Hope it helps – mannuk Oct 31 '13 at 12:51

3 Answers3

13

Looking at your JSON

{
    "subscription": {
        "manager": {
            "username": "admin",
            "password": "admin"
        },
        "userToSubscribe": {
            "username": "newuser",
            "password": "newpassword",
            "email": "user@1and1.es"
        },
        "openid": "myopenid"
    }
}

The root element is subscription and it is a JSON object. Your Subscription class doesn't have a subscription field. So there is nothing to map the subscription element to and it therefore fails with a 400 Bad Request.

Create a class SubscriptionWrapper

public class SubscriptionWrapper {
    private Subscription subscription;

    public Subscription getSubscription() {
        return subscription;
    }

    public void setSubscription(Subscription subscription) {
        this.subscription = subscription;
    }
}

and change your handler method to accept an argument of this type

public @ResponseBody SimpleMessage subscribeUser(@RequestBody SubscriptionWrapper subscriptionWrapper)

You might need to configure the ObjectMapper in MappingJacksonHttpMessageConverter (FYI you should be using MappingJackso2nHttpMessageConverter), so that it ignores missing properties.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • would you mind to explain more your last lines? The configuration of the ObjectMapper and MappingJackson2HttpMessageConverter. In my case the objects has missing properties due to they are not necessary. – mannuk Nov 01 '13 at 12:55
  • @mannuk `MappingJackson2HttpMessageConverter` uses the new Jackson libraries with package `com.fasterxml.jackson`. `ObjectMapper` has a `configure()` method which you can use to configure deserialization and serialization features. You might want to look into some of them. – Sotirios Delimanolis Nov 01 '13 at 13:43
  • do you know any website where i can check it? I would like to learn more about the converter and the configure method. I will test your answer on Monday at work and then I'll be able to tell you my progress – mannuk Nov 01 '13 at 13:52
  • I checked what you recommended me. I created the SusbcriptionWrapper but the 400 error persists. Furthermore, I can't use MappingJackson2HttpMessageConverter because is used by Spring 3.2 but I have to use Spring 3.1. Is there anything i can do? – mannuk Nov 04 '13 at 08:56
  • @mannuk The class is available since Spring `3.1.2`. Use a debugger, step through the `MappingJackson2HttpMessageConverter` and see what field is blocking the deserialization. You can also set your log level to trace to get more details. – Sotirios Delimanolis Nov 04 '13 at 13:26
5

I'm going to answer my own question. First of all special thanks to Sotirios Delimanolis because he gave me the key in order to investigate what it was happening.

As you know, I create the following json from the view:

{"manager":{"username":"admin","password":"admin"},"userToSubscribe":{"username":"newuser","password":"newpassword","email":"user@1and1.es"},"openid":"https://myopenid..."}

I changed it a little bit because I realised that is not necessary to create a object Subscription and a var Subscription. If you build the JSON like this, it will work perfectly:

var manager = {
    username: "admin",
    password: "admin"
};
var userToSubscribe = {
    username: "newuser",
    password: "newpassword",
    email: "user@1and1.es"
};

var openid = "https://myopenid...";

$.ajax({
    url: '/dp/rest/isUserSuscribed.json',
    type: 'POST',
    dataType: 'json',
    contentType: 'application/json',
    mimeType: 'application/json',
    data: JSON.stringify({manager : manager, userToSubscribe : userToSubscribe, openid : openid})   
});

The controller receives this json:

@RequestMapping(method=RequestMethod.POST, value="/isUserSuscribed.json")
public @ResponseBody ResponseMessageElement<Boolean> isUserSuscribed(@RequestBody SubscriptionWrapper subscription){

And the SubscriptionWrapper...

private static class SubscriptionWrapper {
    BasicUser manager;
    BasicUser userToSubscribe;
    String openid;

    public BasicUser getManager() {
        return manager;
    }
    public void setManager(BasicUser manager) {
        this.manager = manager;
    }
    public BasicUser getUserToSubscribe() {
        return userToSubscribe;
    }
    public void setUserToSubscribe(BasicUser userToSubscribe) {
        this.userToSubscribe = userToSubscribe;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
}

So... What is the problem? I was receiving an Incorrect Request 400 error... I debugged the MappingJackson2HttpMessageConverter as Sotirios suggested and there was an exception (No suitable constructor). Jackson Mapping is not able to build an inner class whether this class is not static. Setting SubscriptionWrapper to static was the solution to my problem.

You can also check these answers: http://stackoverflow.com/questions/8526333/jackson-error-no-suitable-constructor

http://stackoverflow.com/questions/12139380/how-to-convert-json-into-pojo-in-java-using-jackson

And if you have problems to deserialize, check this: http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type

Thanks for all the replies.

mannuk
  • 1,259
  • 5
  • 21
  • 43
2

You don't need to do this by yourself. You need to add this dependency in your pom:

<dependencies>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.8.5</version>
    </dependency>
  </dependencies>

After that Spring will do conversion for you.

mvb13
  • 1,514
  • 3
  • 18
  • 33
  • Yes, i have the dependency. It works if i send only the user which has an username, password... but if i try to send the subscription object is not mapped. So one approach if the subscription wrapper does not work is ResponseBody String, or ResponseBody Map and doing it by myself getting the elements and converting to my objects. – mannuk Oct 31 '13 at 10:40
  • I didn't understand one detail. You said that when you send only User object(do you send it to another url?) it works, but when you send whole Subscription this doesn't work. Is this correct? If yes try to check mapping urls first, and HTTP reuest Content-Type specified to json second. – mvb13 Oct 31 '13 at 11:10
  • Could you elaborate how the spring does it. Is the only Jackson integration is possible to convert it to JSON format. OP expect to populate the model with the `@ResponseBody`. –  Oct 31 '13 at 11:19
  • I don't know how Spring does the conversion. I didn't see this in documentation. Documentation only says that you must have jackson dependency added to your project. So this is Spring documentation approach. – mvb13 Oct 31 '13 at 11:25
  • @mvb13 I mean that if i replace the "Subscription" argument in the controller by the argument "User" and I send the json including only the var manager the mapping is ok. – mannuk Oct 31 '13 at 12:53
  • Ok. Show me User class please. – mvb13 Oct 31 '13 at 13:07