Jackson automatic formatting of Joda DateTime to ISO 8601 format - json

According to http://wiki.fasterxml.com/JacksonFAQDateHandling, “DateTime can be automatically serialized/deserialized similar to how java.util.Date is handled.” However, I am not able to accomplish this automatic functionality. There are StackOverflow discussions related to this topic yet most involve a code-based solution, but based upon the quote above I should be able to accomplish this via simple configuration.
Per http://wiki.fasterxml.com/JacksonFAQDateHandling I have my configuration set so that writing dates as timestamps is false. The result is that java.util.Date types are serialized to ISO 8601 format, but org.joda.time.DateTime types are serialized to a long object representation.
My environment is this:
Jackson 2.1
Joda time 2.1
Spring 3.2
Java 1.6
My Spring configuration for the jsonMapper bean is
#Bean
public ObjectMapper jsonMapper() {
ObjectMapper objectMapper = new ObjectMapper();
//Fully qualified path shows I am using latest enum
ObjectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.
WRITE_DATES_AS_TIMESTAMPS , false);
return objectMapper;
}
My test code snippet is this
Date d = new Date();
DateTime dt = new DateTime(d); //Joda time
Map<String, Object> link = new LinkedHashMap<String, Object>();
link.put("date", d);
link.put("createdDateTime", dt);
The resulting snippet of JSON output is this:
{"date":"2012-12-24T21:20:47.668+0000"}
{"createdDateTime": {"year":2012,"dayOfMonth":24,"dayOfWeek":1,"era":1,"dayOfYear":359,"centuryOfEra":20,"yearOfEra":2012,"yearOfCentury":12,"weekyear":2012,"monthOfYear":12 *... remainder snipped for brevity*}}
My expectation is that the DateTime object should matche that of the Date object based upon the configuration. What am I doing wrong, or what am I misunderstanding? Am I reading too much into the word automatically from the Jackson documentation and the fact that a string representation was produced, albeit not ISO 8601, is producing the advertised automatic functionality?

I was able to get the answer to this from the Jackson user mailing list, and wanted to share with you since it is a newbie issue. From reading the Jackson Date FAQ, I did not realize that extra dependencies and registration are required, but that is the case. It is documented at the git hub project page here https://github.com/FasterXML/jackson-datatype-joda
Essentially, I had to add another dependency to a Jackson jar specific to the Joda data type, and then I had to register the use of that module on the object mapper. The code snippets are below.
For my Jackson Joda data type Maven dependency setup I used this:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>${jackson.version}</version>
</dependency>
To register the Joda serialization/deserialization feature I used this:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.
WRITE_DATES_AS_TIMESTAMPS , false);

Using Spring Boot.
Add to your Maven configuration...
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.7.5</version>
</dependency>
Then to your WebConfiguration...
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter
{
public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
{
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
//configure Joda serialization
objectMapper.registerModule(new JodaModule());
objectMapper.configure(
com.fasterxml.jackson.databind.SerializationFeature.
WRITE_DATES_AS_TIMESTAMPS , false);
// Other options such as how to deal with nulls or identing...
objectMapper.setSerializationInclusion (
JsonInclude.Include.NON_NULL);
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}

In Spring Boot the configuration is even simpler. You just declare Maven dependency
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</dependency>
and then add configuration parameter to your application.yml/properties file:
spring.jackson.serialization.write-dates-as-timestamps: false

I thought I'd post an updated working example using:
Spring 4.2.0.RELEASE, Jackson 2.6.1, Joda 2.8.2
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven>
<message-converters>
<beans:bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<beans:property name="objectMapper" ref="objectMapper" />
</beans:bean>
</message-converters>
</annotation-driven>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="objectMapper"
class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<beans:property name="featuresToDisable">
<beans:array>
<util:constant
static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS" />
</beans:array>
</beans:property>
<beans:property name="modulesToInstall"
value="com.fasterxml.jackson.datatype.joda.JodaModule" />
</beans:bean>
<beans:bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<beans:property name="defaultLocale" value="en" />
</beans:bean>
<!-- Configure the Message Locale Resources -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="errors" />
</beans:bean>
<beans:bean id="versionSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="version" />
</beans:bean>
<!-- DataSource -->
<beans:bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<beans:property name="jndiName" value="java:comp/env/jdbc/TestDB" />
</beans:bean>
<!-- POJO: Configure the DAO Implementation -->
<beans:bean id="publicationsDAO"
class="com.test.api.publication.PublicationsDAOJdbcImpl">
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- Things to auto-load -->
<context:component-scan base-package="com.test.api" />
<context:component-scan base-package="com.test.rest" />
</beans:beans>
API Code
package com.test.api.publication;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName("event")
#JsonIgnoreProperties(ignoreUnknown=true)
public class Publication {
private Map<String, Object> tokens;
private String href;
private String policy_path;
#JsonProperty("tokens")
public Map<String, Object> getTokens() {
return tokens;
}
#JsonProperty("tokens")
public void setTokens(Map<String, Object> tokens) {
this.tokens = tokens;
}
#JsonProperty("href")
public String getHref() {
return href;
}
#JsonProperty("href")
public void setHref(String href) {
this.href = href;
}
#JsonProperty("policyPath")
public String getPolicyPath() {
return policy_path;
}
#JsonProperty("policyPath")
public void setPolicyPath(String policy_path) {
this.policy_path = policy_path;
}
}
package com.test.api.publication;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PublicationsDAOJdbcImpl implements PublicationsDAO{
static final Logger logger = LoggerFactory.getLogger(PublicationsDAOJdbcImpl.class.getName());
private DataSource _dataSource;
#Override
public void setDataSource(DataSource ds) {
// TODO Auto-generated method stub
}
#Override
public void close() {
// TODO Auto-generated method stub
}
#Override
public Publication getPublication(String policyPath) {
Publication ret = new Publication();
//TODO: do something
return ret;
}
}
package com.test.rest.publication;
import java.util.HashMap;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.test.api.publication.Publication;
import com.test.api.publication.PublicationsDAO;
import com.test.rest.error.UnknownResourceException;
#RestController
#RequestMapping("/pub")
public class PublicationController {
private static final Logger logger = LoggerFactory.getLogger(PublicationController.class);
#Autowired
#Qualifier("publicationsDAO")
private PublicationsDAO publicationsDAO;
/**********************************************************************************************************************
*
* #param policyPath
* #return
* #throws UnknownResourceException
*/
#RequestMapping(value = "/{policyPath}", method = RequestMethod.GET)
public Publication getByPolicyPath(#PathVariable String policyPath) throws UnknownResourceException{
logger.debug("policyPath=" + policyPath);
Publication ret = publicationsDAO.getPublication(policyPath);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("TEST1", null);
map.put("TEST2", new Integer(101));
map.put("TEST3", "QuinnZilla");
map.put("TEST4", new DateTime());
ret.setTokens(map);
return ret;
}
}
And I get the output result
{
"tokens": {
"TEST2": 101,
"TEST3": "QuinnZilla",
"TEST4": "2015-10-06T16:59:35.120Z",
"TEST1": null
},
"href": null,
"policyPath": null
}

Related

JSON reponse with Spring Controllers in Jetty vs Tomcat

I am building a simple Spring MVC webapp and was developing on jetty. My controller binding used this:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody String getDoc
And returning a String from a JSONObject correctly resolves to JSON in my ajax response.
But using those same controllers, i deployed my gradle war to tomcat and my json came back wrapped as true strings.
So i changed my headers to use Map and that seems to fix things in both jetty and tomcat:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody Map<String, String> getDoc
I convert from the string to a map with this:
HashMap<String, String> jsonResponse = new HashMap<String, String>();
if(claimFolder.has("error")){
response.setStatus(500);
}else{
jsonResponse = new ObjectMapper().readValue(claimFolder.toString(), HashMap.class);
}
return jsonResponse;
My question is why this is nessesary?
Here's my jackson converter configuration:
<bean id="formConverter" class="org.springframework.http.converter.FormHttpMessageConverter" />
<!-- add byte[] converter -->
<bean id="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes" value="application/octet-stream" />
</bean>
<!-- add in our JSON message converter -->
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json;charset=UTF-8" />
</bean>
<!-- add in our plain string message converter -->
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>
<!-- Expose the authenticated handler to all beans that have been declared via annotation -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>
TL;DR: Why does jetty and tomcat return stringified JSON differently?
Well, it's absolutely normal for Spring content negotiation to translate a String object as a simple string without marshalling it to a JSON object. In order to serialize a java String object in JSON object you need to wrap it previously in some java class. For example:
QuestionStatus {
private String status;
public QuestionStatus(String status) {
this.status = status;
}
public getStatus() {
return status;
}
}
Hence you have to return in your Controller method not a String but QuestionStatus.

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.

Jersey JAXB Based JSON support

I am trying to use Jersey's capabilities to produce JSON from my web-service methods.
Everything worked well but then I discovered that for a list of objects JSON representation contains something like enclosing root tag. I found out that I can configure JAXB Based JSON support with JSONConfiguration.natural() to produce a desirable result. So I wrote the following
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private final JAXBContext context;
private final Set<Class> types;
private final Class[] cTypes = {TrRegion.class};
public JAXBContextResolver() throws Exception {
this.types = new HashSet(Arrays.asList(cTypes));
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
return (types.contains(objectType)) ? context : null;
}
}
And plugged it in like this
public class WebServiceApplication extends Application {
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(OrderInfrastructureResource.class);
resources.add(OrderResource.class);
resources.add(JAXBContextResolver.class);
return resources;
}
}
<servlet>
<description>JAX-RS Tools Generated - Do not modify</description>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.[...].WebServiceApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
But for some reason I always get
java.lang.IllegalStateException: No JAXB provider found for the following JAXB context: class com.sun.xml.bind.v2.runtime.JAXBContextImpl
at com.sun.jersey.json.impl.JSONHelper.getJaxbProvider(JSONHelper.java:106) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.JSONHelper.getJaxbProvider(JSONHelper.java:106) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.DefaultJaxbXmlDocumentStructure.getXmlDocumentStructure(DefaultJaxbXmlDocumentStructure.java:76) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.writer.Stax2JacksonWriter.<init>(Stax2JacksonWriter.java:169) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.Stax2JsonFactory.createWriter(Stax2JsonFactory.java:105) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.provider.entity.JSONListElementProvider.writeList(JSONListElementProvider.java:133) [jersey-json-1.17.jar:1.17]
Can someone tell me why?
When I change the line
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
to
this.context = new JSONJAXBContext(JSONConfiguration.mapped().build(), cTypes);
it begins to work but gives enclosing root tag(well it is the same as not specifying any ContextResolver). Strange.(Strange meaning, that the difference is only in mapping type I provide).
I try to run my app on Jboss 7.1.1 with Restesy disabled(I have removed lines <extension module="org.jboss.as.jaxrs"/> and <subsystem xmlns="urn:jboss:domain:jaxrs:1.0"/> from my standalone.xml file). Also I use com.sun.jersey.spi.spring.container.servlet.SpringServlet as Jersey servlet.
Please, tell me what am I missing.
What could be the problem?
Apparently I was missing jaxb-impl library for my application so I just added jBoss' com.sun.xml.bind module in my jboss-deployment-structure file as following:
<dependencies>
<module name="com.sun.xml.bind" />
<module name="org.codehaus.jackson.jackson-core-asl" />
<module name="org.codehaus.jackson.jackson-jaxrs" />
<module name="org.codehaus.jackson.jackson-mapper-asl" />
<module name="org.codehaus.jackson.jackson-xc" />
</dependencies>

Spring #ResponseBody Jackson JsonSerializer with JodaTime

I have below Serializer for JodaTime handling:
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
private static final String dateFormat = ("MM/dd/yyyy");
#Override
public void serialize(DateTime date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = DateTimeFormat.forPattern(dateFormat).print(date);
gen.writeString(formattedDate);
}
}
Then, on each model objects, I do this:
#JsonSerialize(using=JodaDateTimeJsonSerializer.class )
public DateTime getEffectiveDate() {
return effectiveDate;
}
With above settings, #ResponseBody and Jackson Mapper sure works. However, I don't like the idea where I keep writing #JsonSerialize. What I need is a solution without the #JsonSerialize on model objects. Is it possible to write this configuration somewhere in spring xml as a one configuration?
Appreciate your help.
Although you can put an annotation for each date field, is better to do a global configuration for your object mapper. If you use jackson you can configure your spring as follow:
<bean id="jacksonObjectMapper" class="com.company.CustomObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" >
</bean>
For CustomObjectMapper:
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setDateFormat(new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)"));
}
}
Of course, SimpleDateFormat can use any format you need.
#Moesio pretty much got it. Here's my config:
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven/>
<!-- Instantiation of the Default serializer in order to configure it -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterConfigurer" init-method="init">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</list>
</property>
</bean>
<bean id="jacksonObjectMapper" class="My Custom ObjectMapper"/>
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
The bit that got me is that <mvc:annotation-driven/> makes its own AnnotationMethodHandler and ignores the one you make manually. I got the BeanPostProcessing idea from http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html to configure the one that gets used, and voilà! Works like a charm.
Same using JavaConfig of Spring 3:
#Configuration
#ComponentScan()
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters)
{
converters.add(0, jsonConverter());
}
#Bean
public MappingJacksonHttpMessageConverter jsonConverter()
{
final MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setObjectMapper(new CustomObjectMapper());
return converter;
}
}
If you are using Spring Boot, try this in application.yml :
spring:
jackson:
date-format: yyyy-MM-dd
time-zone: Asia/Shanghai
joda-date-time-format: yyyy-MM-dd
If you simply have the Jackson JARs on your classpath, and return a #ResponseBody, Spring will automatically convert the Model object to JSON. You don't need to annotate anything in the Model to get this to work.

Spring mvc rest, json and xml output contain the package name

We need to support XML and JSON output from a objects, I have a List which i display in json and xml format, the problem i am facing is it also shows the underlying class, below i am showing all part of codes starting with
output
domain objects
configuration &
controller
**XML Output**
"<**com.bookstore.Books**> Unwanted
<books>
<com.bookstore.Book>
<name>book0</name>
</com.bookstore.Book>
<com.bookstore.Book>
<name>book1</name>
</com.bookstore.Book>
</books>
</com.bookstore.Books>"
**JSON Output** Unwanted part is in bold
**"org.springframework.validation.BindingResult.books**"
:[{"isbn":"03601","authors":[{"autho
I have defind book class as
#XStreamAlias("book")
public class Book {....}
and Books
#XStreamAlias("books")
public class Books {...
List<Book> books;
The context settings are like this
<beans:bean id="xmlView"
class="org.springframework.web.servlet.view.xml.MarshallingView">
<beans:constructor-arg>
<beans:bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<beans:property name="autodetectAnnotations" value="true"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="jsonView"
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
</beans:bean>
<beans:bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
</beans:bean>
The controller part is :
#RequestMapping(value = "/books/xml")
public ModelAndView getAllBooksXML() {
List<Book> books = bookService.getAllBooks();
ModelAndView mav =
new ModelAndView("xmlView", BindingResult.MODEL_KEY_PREFIX + "books", books);
return mav;
}
#RequestMapping(value = "/books/json")
public ModelAndView getAllBooksJson() {
List<Book> books = bookService.getAllBooks();
ModelAndView mav =
new ModelAndView("jsonView", BindingResult.MODEL_KEY_PREFIX + "books", books);
return mav;
}
Please let me know otherwie i will have t
i write a custom converter.
The code you provided seems to use XStream annotations while your spring configuration is configured to use jackson.
Have you tried the annotations supported by jackson?
https://github.com/FasterXML/jackson-annotations/wiki/JacksonAnnotations