Spring MVC @RequestBody receive an Object wrapper with non-primitive attributes
Solution 1
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":"[email protected]"},"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: "[email protected]"
};
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.
Solution 2
Looking at your JSON
{
"subscription": {
"manager": {
"username": "admin",
"password": "admin"
},
"userToSubscribe": {
"username": "newuser",
"password": "newpassword",
"email": "[email protected]"
},
"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.
Solution 3
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.
mannuk
Updated on November 15, 2020Comments
-
mannuk over 3 years
I create the JSON as follows:
var manager = { username: "admin", password: "admin" }; var userToSubscribe = { username: "newuser", password: "newpassword", email: "[email protected]" }; 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":"[email protected]"},"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!
-
M. Deinum over 10 yearsThere 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. -
Admin over 10 yearsThe wrapper is an absolute different concept in Java, what you have a couple of objects that you want to aggregate in the outer class.
-
pappu_kutty over 10 yearswhat version of spring you are using?
-
Will Keeling over 10 yearsCan you post the mapping for that controller method and the code that is sending the JSON?
-
mannuk over 10 yearsGive a minute I'm going to update the post
-
mannuk over 10 yearsThe post is now updated. I included my pom dependencies, spring version and the converters. Hope it helps
-
-
mannuk over 10 yearsYes, 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<String,Object> and doing it by myself getting the elements and converting to my objects.
-
mvb13 over 10 yearsI 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.
-
Admin over 10 yearsCould 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
. -
mvb13 over 10 yearsI 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.
-
mannuk over 10 years@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.
-
mvb13 over 10 yearsOk. Show me User class please.
-
mannuk over 10 yearswould 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.
-
Sotirios Delimanolis over 10 years@mannuk
MappingJackson2HttpMessageConverter
uses the new Jackson libraries with packagecom.fasterxml.jackson
.ObjectMapper
has aconfigure()
method which you can use to configure deserialization and serialization features. You might want to look into some of them. -
mannuk over 10 yearsdo 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 over 10 yearsI 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?
-
Sotirios Delimanolis over 10 years@mannuk The class is available since Spring
3.1.2
. Use a debugger, step through theMappingJackson2HttpMessageConverter
and see what field is blocking the deserialization. You can also set your log level to trace to get more details.