Spring 3.0 exception converting String to java.util.Date on POST - json

I'm hoping someone can help me since I have been banging my head against a wall for a couple of days on a issue which seems straightforward and which has been documented in other threads on the web.
I am using Smart GWT client (3.0) in conjunction with Spring 3.1 server and using JSON to communicate (with Jackson API 1.9).
The issue is that when I attempt to save a date from my SmartGWT client and it is sent to the server I get the following exception:
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'comment' on field 'dateAdded': rejected value [2012-06-27T10:57:47+0100]; codes [typeMismatch.comment.dateAdded,typeMismatch.dateAdded,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [comment.dateAdded,dateAdded]; arguments []; default message [dateAdded]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'dateAdded'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.util.Date for value '2012-06-27T10:57:47+0100'; nested exception is java.lang.IllegalArgumentException]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:110)
I have seen this issue in a few other posts, but most relate to not having formatted the Date in the correct format, but I have tried various formats:
- yyyy-MM-dd
- yyyy-MM-dd'T'HH:mm:ssZ
- yyyyMMddHHmmssZ (as per suggestion here: http://code.google.com/p/usersapi/issues/detail?id=8)
So in my code I have done the following:
Configured a CustomObjectMapper:
`
public class CustomObjectMapper extends ObjectMapper {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
public CustomObjectMapper() {
super();
configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setDateFormat(formatter);
getDeserializationConfig().setDateFormat(formatter);
}
}
`
Spring app context thusly:
`
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
<property name="supportedMediaTypes" value="application/json" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<context:component-scan base-package="com.jpmorgan.creditriskreporting.server" />
<bean id="marshallingConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="com.jpmorgan.creditriskreporting.server.util.CustomObjectMapper" />
<!-- Client -->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="marshallingConverter" />
<ref bean="jsonConverter" />
</list>
</property>
</bean>
`
Bean object:
`
import java.util.Date;
#JsonAutoDetect
public class Comment {
private int id;
private String comment;
private Date dateAdded;
public Comment() {}
public Comment(int id) {
this.id = id;
}
...
//#JsonSerialize(using=JsonDateSerializer.class) -- I had previously tried to use these custom Date serializer class
public Date getDateAdded() {
return dateAdded;
}
//#JsonDeserialize(using=JsonDateDeserializer.class)
public void setDateAdded(Date dateAdded) {
this.dateAdded = dateAdded;
}
`
EDIT:
Controller Class
This may be where the issue lies, since when I use #RequestBody it works from my Integration tests, however, my Abstract RestDataSource in SmartGWT only works with #ModelAttribute, so I'm not sure how to proceed.
#RequestMapping(value="/", method=RequestMethod.POST)
public #ResponseBody Comment createNewComment2(#ModelAttribute Comment comment) {
log.info("calling createComment with comment: {}", comment);
comment.setDateAdded(new Date());
Comment added = commentDao.create(comment);
log.info("created comment: {}", added);
return commentDao.get(comment);
}
So I can fetch data from the server and the date is displayed in SmartGWT fine. It's only when I do the add data that I get the issue. From Smart GWT Developer Console:
{
"dataSource":"CommentDS",
"operationType":"add",
"componentId":"isc_DynamicForm_1",
"data":{
"userAdded":"sharper",
"dateAdded":"2012-06-27T10:57:47+0100",
"comment":"sample"
},
"callback":{
"target":[DynamicForm ID:isc_DynamicForm_1],
"methodName":"saveEditorReply"
},
"showPrompt":true,
"prompt":"Saving form...",
"oldValues":{
},
"clientContext":{
},
"requestId":"CommentDS$6272"
}
Any help with this is hugely appreciated.
Cheers,
Steve

I found out the issue thanks to http://vkubushyn.wordpress.com/2011/05/31/smart-gwt-restful-spring-mvc
Had to use Spring's InitBinder
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

You should add DateFormat into your model.
#DateTimeFormat(pattern = "dd.MM.yyyy")
private Date beginDate;
#DateTimeFormat(pattern = "dd.MM.yyyy")
private Date endDate;
as a function parameter
void functionName** (#RequestParam("beginDate") #DateTimeFormat(pattern = "dd.MM.yyyy")Date beginDate, #RequestParam("endDate") #DateTimeFormat(pattern = "dd.MM.yyyy")Date endDate)

I might be wrong, but as far as I remember the Z stands for timezone in ISOwhoknowswhatformat. And that's 4 chars wide, so I would try this:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ");
By the way: if this is the issue you should've catched it in your unit tests. You do have unit test for CustomObjectMapper don't you? :P

Related

Custom ObjectMapper and NamingStrategy in Spring 3 MVC

I'm using Spring MVC 3 and MappingJacksonHttpMessageConverter in order to get the json data with #ResponseBody. With the default config works ok but now i need to transform the camelCase fields to Pascal casing. For this purpose, i've developed a custom naming strategy:
UpperCaseNamingStrategy.java
public class UpperCaseNamingStrategy extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig config, AnnotatedField field, String defaultName){
return convert(defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName){
return convert(defaultName);
}
#Override
public String nameForSetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName){
return convert(defaultName);
}
public String convert(String defaultName){
char[] arr= defaultName.toCharArray();
if(arr.length != 0){
if(Character.isLowerCase(arr[0])){
arr[0] = Character.toUpperCase(arr[0]);
}
}
return new StringBuilder().append(arr).toString();
}
}
I set my custom strategy to the objectMapper and i set the objectMapper in the converter. These are the beans:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper">
<property name="propertyNamingStrategy" ref="namingStrategy"/>
</bean>
<bean id="namingStrategy" class="es.unican.meteo.util.UpperCaseNamingStrategy"></bean>
The beans are registered properly because i can see it in the log but when i request the json data the behaviour is the same and the converter method is not called. Do I need more configs?
Following changes are suggested as compared to what I did in my project:
Change mapper bean class to "com.fasterxml.jackson.databind.ObjectMapper". I am using Spring 4.3
add #JsonProperty annotation to the property of class which is being serielized/deseralized
Create default constructors in class which is being serielized/deseralized
Best of Luck!

smartgwt restdatasource json date validation

I am using Spring 3.2 MVC Controller and a Spring-WS to create a RESTful web-service. The Spring controller accepts an object files an update to the database correctly and then returns JSON to the front-end. The Spring Context is set for message converts for JSON. I have Unit Tests for these, so I know the Spring Controllers are working and are filing data accordingly.
The error, actually a warning, comes when I get the data/JSON back from the web-service:
10:05:08.906[ERROR[Phonebook]10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate:value:-99187200000 failed on validator {type:"isDate",typeCastValidator:true,_generated:true,defaultErrorMessage:"Must be a date."}
com.smartgwt.client.core.JsObject$SGWT_WARN: 10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate: value: -99187200000 failed on validator: {type: "isDate",typeCastValidator: true,_generated: true,defaultErrorMessage: "Must be a date."}
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
at java.lang.Thread.run(Thread.java:662)
So, here is my UserDataSource:
package com.opensource.restful.client.datasource;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.JavaScriptObject;
import com.opensource.restful.shared.Constants;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.OperationBinding;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.data.fields.DataSourceBooleanField;
import com.smartgwt.client.data.fields.DataSourceDateField;
import com.smartgwt.client.data.fields.DataSourceIntegerField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.DSDataFormat;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.DSProtocol;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.util.JSON;
public class UserDataSource extends RestDataSource
{
private static UserDataSource instance = null;
public static UserDataSource getInstance()
{
if (instance == null)
{
instance = new UserDataSource("restUserDS");
}
return instance;
}
private UserDataSource(String id)
{
setID(id);
setClientOnly(false);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.GETPARAMS);
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("GET");
fetch.setRequestProperties(fetchProps);
// set up ADD to use POST requests
OperationBinding add = new OperationBinding();
add.setOperationType(DSOperationType.ADD);
add.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest addProps = new DSRequest();
addProps.setHttpMethod("POST");
// addProps.setContentType("application/json");
add.setRequestProperties(addProps);
// set up UPDATE to use PUT
OperationBinding update = new OperationBinding();
update.setOperationType(DSOperationType.UPDATE);
update.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest updateProps = new DSRequest();
updateProps.setHttpMethod("PUT");
// updateProps.setContentType("application/json");
update.setRequestProperties(updateProps);
// set up REMOVE to use DELETE
OperationBinding remove = new OperationBinding();
remove.setOperationType(DSOperationType.REMOVE);
DSRequest removeProps = new DSRequest();
removeProps.setHttpMethod("DELETE");
remove.setRequestProperties(removeProps);
// apply all the operational bindings
setOperationBindings(fetch, add, update, remove);
init();
}
private DataSourceIntegerField userIdField;
private DataSourceBooleanField userActiveField;
private DataSourceTextField usernameField;
private DataSourceTextField passwordField;
private DataSourceTextField firstnameField;
private DataSourceTextField lastnameField;
private DataSourceTextField emailField;
private DataSourceTextField securityQuestion1Field;
private DataSourceTextField securityAnswer1Field;
private DataSourceTextField securityQuestion2Field;
private DataSourceTextField securityAnswer2Field;
private DataSourceDateField birthdateField;
private DataSourceIntegerField positionIdField;
protected void init()
{
setDataFormat(DSDataFormat.JSON);
setJsonRecordXPath("/");
// set the values for the datasource
userIdField = new DataSourceIntegerField(Constants.USER_ID, Constants.TITLE_USER_ID);
userIdField.setPrimaryKey(true);
userIdField.setCanEdit(false);
userActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
usernameField = new DataSourceTextField(Constants.USER_USERNAME, Constants.TITLE_USER_USERNAME);
passwordField = new DataSourceTextField(Constants.USER_PASSWORD, Constants.TITLE_USER_PASSWORD);
firstnameField = new DataSourceTextField(Constants.USER_FIRST_NAME, Constants.TITLE_USER_FIRST_NAME);
lastnameField = new DataSourceTextField(Constants.USER_LAST_NAME, Constants.TITLE_USER_LAST_NAME);
emailField = new DataSourceTextField(Constants.USER_EMAIL, Constants.TITLE_USER_EMAIL);
securityQuestion1Field =
new DataSourceTextField(Constants.USER_SECURITY_QUESTION_1, Constants.TITLE_USER_SECURITY_QUESTION_1);
securityAnswer1Field =
new DataSourceTextField(Constants.USER_SECURITY_ANSWER_1, Constants.TITLE_USER_SECURITY_ANSWER_1);
securityQuestion2Field =
new DataSourceTextField(Constants.USER_SECURITY_QUESTION_2, Constants.TITLE_USER_SECURITY_QUESTION_2);
securityAnswer2Field =
new DataSourceTextField(Constants.USER_SECURITY_ANSWER_2, Constants.TITLE_USER_SECURITY_ANSWER_2);
birthdateField = new DataSourceDateField(Constants.USER_BIRTHDATE, Constants.TITLE_USER_BIRTHDATE);
positionIdField = new DataSourceIntegerField(Constants.USER_POSITION_ID, Constants.TITLE_USER_POSITION_ID);
// positionActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
// positionCodeField;
// positionDescriptionField;
setFields(userIdField, userActiveField, usernameField, passwordField, firstnameField, lastnameField,
emailField, birthdateField, securityQuestion1Field, securityAnswer1Field, securityQuestion2Field,
securityAnswer2Field, positionIdField);
setFetchDataURL(getServiceRoot() + "/userId/{id}"); // works great
setAddDataURL(getServiceRoot() + "/create");
setUpdateDataURL(getServiceRoot() + "/update");
setRemoveDataURL(getServiceRoot() + "/remove"); // works great
}
protected String getServiceRoot()
{
return "rest/users";
}
protected String getPrimaryKeyProperty()
{
return "userId";
}
#Override
protected Object transformRequest(DSRequest dsRequest)
{
System.out.println("UserDataSource: transformRequest: START");
dsRequest.setContentType("application/json");
JavaScriptObject jso = dsRequest.getData();
String jsoText = JSON.encode(jso);
System.out.println("UserDataSource: transformRequest: START: jsoText=" + jsoText);
// ================================================================================
// String strDob = JSOHelper.getAttribute(jso, Constants.USER_BIRTHDATE);
// Date dateDob = JSOHelper.getAttributeAsDate(jso, Constants.USER_BIRTHDATE);
// JSOHelper.setAttribute(jso, Constants.USER_BIRTHDATE, dateDob.getTime());
// System.out.println("UserDataSource: transformRequest: START2: jsoText2=" + jsoText);
// ================================================================================
// get the user position id which comes from the UI
// the name of this field from the UI 'userPositionId'
String userPositionId = JSOHelper.getAttribute(jso, Constants.USER_POSITION_ID);
// create a small JavaScriptObject to be used for the position
// the JSON string would look like {"id":x} x = userPositionId
Map mapPositionId = new HashMap();
mapPositionId.put("id", userPositionId);
JavaScriptObject jsoPositionId = JSOHelper.convertMapToJavascriptObject(mapPositionId);
// This creates the new JSON attribute:
// ... , "position":{"id":x}
JSOHelper.setAttribute(jso, "position", jsoPositionId);
// remove the JSON Attribute: ... , "userPositionId":x
JSOHelper.deleteAttribute(jso, Constants.USER_POSITION_ID);
String s1 = JSON.encode(jso);
System.out.println("UserDataSource: transformRequest: FINISH: s1=" + s1);
return s1;
// return super.transformRequest(dsRequest);
}
protected void transformResponse(DSResponse response, DSRequest request, Object data)
{
System.out.println("UserDataSource: transformResponse: START");
super.transformResponse(response, request, data);
System.out.println("UserDataSource: transformResponse: FINISH");
}
}
I can confirm I am sending data/JSON just fine. I have to make a slight change to add an attribute that I am sending back. And I believe that is the purpose of TransformRequest.
The Spring MVC Controller receiving the Update looks like:
#RequestMapping(value="/update",
method=RequestMethod.PUT,produces="application/json",
headers="content-type=application/json")
public #ResponseBody UserDTO updateUser(#RequestBody UserDTO user)
{
System.out.println("UserController: START: updateUser: user=" + user);
UserEntity userEntity = service.update(user);
UserDTO userDto = Mapping.mappingUser(userEntity);
System.out.println("UserController: FINISH: updateUser: userDto=" + userDto);
return userDto;
}
And I can confirm I am getting a valid UserDTO. When I look at the transformResponse:
System.out.println("UserDataSource: transformResponse: START");
super.transformResponse(response, request, data);
System.out.println("UserDataSource: transformResponse: FINISH");
I get the error on the first println, I haven't even done the super.transformResponse just yet. When I look at the data coming back, this is the JSON I am getting back.
{
"userId":1,
"userActive":true,
"position":{
"id":1,
"active":true,
"code":"ADMIN",
"description":"Administrator"
},
"username":"demo",
"password":"demo",
"otherPassword":null,
"userFirstName":"DemoXXX",
"userLastName":"DemoXXX",
"userEmail":"tom#tomholmes.netXXX",
"userSecurityQuestion1":"Meaning of Life?XXX",
"userSecurityAnswer1":"42XX",
"userSecurityQuestion2":"aaaXX",
"userSecurityAnswer2":"bbbXX",
"userBirthDate":-99100800000,
"contacts":[
{
"contactId":2,
"userId":1,
"prefix":"Mr.",
"firstName":"updated_fn",
"middleName":null,
"lastName":"updated_ln",
"suffix":"Jr.",
"address1":"123 main street",
"address2":"Apt. 456",
"city":"Randolph",
"state":"MA",
"zip":"12345-1234",
"companyId":0,
"enteredBy":0,
"enteredDate":null,
"editedBy":0,
"editedDate":null,
"birthDate":null,
"emails":null,
"phones":null,
"links":null
}
],
"userPositionId":null
}
So ... How do I fix my datasource or transformResponse to remove this warning? The JSON appears to be correct, and the only issue is with the "userBirthDate" when it comes back as a long negative number, I presume the milliseconds from the epoch. Is there some change I can make in the JSON/Jackson Mapper to change how the dates are formatted?
Thanks for any help!
UPDATE 1:
The help provided below was helpful, and now I know this is not a SmartGWT or RestDataSource issue and is strictly with how jackson converts a java.util.Date within an object. The conversion changes dates to a negative long number and should have another format. I am using Spring 3.2 and was using the old Jackson 1.9.14. But now, I upgraded to Jackson 2, and my pom.xml now uses:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.1.4</version>
</dependency>
Within my spring-servlext.xml:
<context:component-scan base-package="com.opensource.restful" />
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json"/>
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter"/>
</list>
</property>
</bean>
<mvc:annotation-driven />
I have been Googling for a few hours now and looking for a solution that uses the Jackson2 mapper within the Spring Configuration, and after I make sure I get all the bean definitions correct, the userBirthDate is still coming back as a negative long. I am sure this configuration can be tweaked just a bit to get it the way I want, so the date comes back as the ISO format: yyyy-MM-dd'T'HH:mm:ssZ
Thanks for helping me get closer.
UPDATE 2:
I think I did it. As previously stated, I upgraded to Jackson2 which I understand is already part of Spring 3.2, which is the version of Spring I am using.
The spring-servlet.xml that I am using, and which does work looks like:
<context:component-scan base-package="com.opensource.restful" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>
I had to add MappingJackson2HttpMessageConverter the second time because, it's referenced in the restTemplate ... but If I could just define it once, that would be fine. So, maybe someone can help me define the spring-servlet.xml better.
Anyway, this change works and as a result the JSON date comes back as:
"userBirthDate":"1966-11-03T00:00:00-0500"
so, that's progress so far.
From the validation error - defaultErrorMessage:"Must be a date"
Since birthdateField is DataSourceDateField, your UserDTO.userBirthDate must be a java.util.Date or similar and have Date getUserBirthDate().
And Constants.USER_BIRTHDATE must be set to "userBirthDate".
If all above is alright, its due to default serialization of java.util.Date object to JSON.
Check following for additional information on that.
http://java.dzone.com/articles/how-serialize-javautildate (Do not use static SimpleDateFormat)
Spring 3.1 JSON date format
jackson2 JSON ISO 8601 date from JodaTime in Spring 3.2RC1
SmartGWT works best when following date format is used (e.g.- 2013-05-09T00:00:00).
yyyy-MM-dd'T'HH:mm:ss
System.out.println() can not be used in SmartGWT/GWT as client side code is converted to JavaScript and run inside the browser, without a JVM.
You probably won't need to use transformResponse() in this case.

Can #JsonTypeInfo be used with Collections?

Using Spring 3 and Jackson 1.7.6, I can serialize implementations of an abstract class and output the fully-qualified name of the class as a property called #class. This works fine when my Spring controllers return a single instance from a controller annotated with #ResponseBody.
When returning a Collection of the above types the resulting JSON changes according to which type is being serialized (fields from each subclass are present), but it does not include the #class property, which our client code needs.
How can I get this type hint into the serialized JSON when returning a collection?
//Returns complete with #class=com.package.blah
#RequestMapping("/json/getProduct.json")
public #ResponseBody Product getProduct(Integer id)
{
return service.getProduct(id);
}
//Does not include #class
#RequestMapping("/json/getProducts.json")
public #ResponseBody List<Product> getProducts()
{
return service.getProducts();
}
In order to do this you will need to configure ObjectMapper. This is not straightforward via Spring, as rather than settable properties, ObjectMapper has invokable methods that set its state (and then it stores this as a bitmask).
If you are using <mvc:annotation-driven /> you will need to replace it with the equivalent markup, which can be found in the Spring JavaDocs.
Extend ObjectMapper:
public class ConfigurableObjectMapper extends ObjectMapper
{
public ConfigurableObjectMapper()
{
this.enableDefaultTypingAsProperty(DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.Id.CLASS.getDefaultPropertyName());
}
}
Then tell Spring to use an instance of this class instead of the default implementation.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.blitzgamesstudios.web.common.json.ConfigurableObjectMapper" />
</property>
</bean>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
You can use #JsonTypeInfo with POJOs, Collections and Maps, but note that the declared value type of Collections and Maps must be one that has (or inherits) #JsonTypeInfo annotation (when using per-class #JsonTypeInfo annotation). This would not work, for example, if you have type like "Collection" -- in this case, Deejay's answer is correct, as you can force inclusion with "default typing" option.
But things should also work if you have a Collection property to serialize/deserialize, i.e.:
public class Bean {
#JsonTypeInfo(....)
public Collection<Object> listOfObjects; // does work because it's per-property annotation!
// ... also, applies to value type and not Collection type itself
}
since that will override any #JsonTypeInfo annotations value type might otherwise have
I had the problem withjava.util.Map, so I did something like:
public interface MyMap extends Map<Long, Product> {}
and
public class MyHashMap extends HashMap<Long, Product> implements MyMap {}
Found on: http://jackson-users.ning.com/forum/topics/mapper-not-include-type-information-when-serializing-object-why
Object mapper bean can enable default typing:
ObjectMapper mapper = new ObjectMapper()
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
This will give the json output as following:
[
"java.util.ArrayList",
[
{
"#class": "com.xyz.Product",
"name": "myName"
}
]
]

Spring MVC POST #RequestBody don't bind to JSON string in the http request

this is my signature of the POST method of my Spring MVC controller
#RequestMapping(value="/createNewGame", method=RequestMethod.POST)
public ModelAndView createNewGame(#RequestParam(value="phoneNumber") String param,#RequestBody final SampleDTO sampleDTO) {
Map model2 = new HashMap();
model2.put("firstname", "Peter");
model2.put("secondname", "Schmitt");
return new ModelAndView("jsonView", model2);
}
instead this is the definition of the SampleDTO class:
public class SampleDTO implements Serializable{
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I'm not able to execute the request for this method. I have this error from the client:
org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.supports(Ljava/lang/Class;)Z
after execute this POST request with RestClient app with these parameters:
http://localhost:8080/SpringExample5/createNewGame.json?phoneNumber=6 (POST)
Content-Type application/json (Header attribute)
{ "value": "a" } (Body)
This is also the configuration of Spring in my web app:
<bean name="/gameController.json"
class="com.alu.server.games.acquisition.controllers.GameController"/>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
<bean id="jsonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" >
<property name="objectMapper">
<ref bean="JacksonObjectMapper" />
</property>
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="objectMapper">
<ref bean="JacksonObjectMapper" />
</property>
</bean>
<bean id="JacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
Someone can help me in order to find the problem?
Thanks in advance !
In the setting I used I did specify the Media Type to be "application/json" both in the web service's annotation as well as the XML configuration for json message converter.
Please checkout my FAQ on the matter here for further details.

Jackson serializationConfig

I am using Jackson JSON in a Spring 3 MVC app. To not serialize each and every single Date field, I created a custom objectmapper that uses a specific DateFormat:
#Component("jacksonObjectMapper")
public class CustomObjectMapper extends ObjectMapper
{
Logger log = Logger.getLogger(CustomObjectMapper.class);
#PostConstruct
public void afterProps()
{
log.info("PostConstruct... RUNNING");
//ISO 8601
getSerializationConfig().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SZ"));
}
//constructors...
}
This custom ObjectMapper is injected into the JsonConverter:
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" /> <!-- defined in CustomObjectMapper -->
</bean>
There is no exception in the logs and serialization works, but it is not picking up the dateformat, it simple serializes to a timestamp. The #PostConstruct annotation works, the log statement in the method is in the logs.
Does anyone know why this fails?
You may also need to specify that you want textual Date serialization, by doing:
configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
(although I was assuming setting non-null date format might also trigger it, but maybe not)
Also, you can do configuration of mapper directly from constructor (which is safe). Not that it should change behavior, but would remove need for separate configuration method.
I've done the below which works to get around compatability with Java / PHP timestamps. Java uses milliseconds since EPOCH and PHP uses seconds so was simpler to use ISO dates.
I declare the below message adapters:
<bean id="messageAdapter"
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean id="jacksonJsonMessageConvertor"
class="my.app.MyMappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
And MyMappingJacksonHttpMessageConverter looks like the below:
public class MyMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter {
public MyMappingJacksonHttpMessageConverter(){
super();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setObjectMapper(objectMapper);
}
}
With the above all dates are written out in ISO format.
For Spring config application.properties
spring.jackson.serialization.fail-on-empty-beans=false