7

I'm using jersey and jackson together to develop my REST API, and I'm having a problem when deserializing a date string. I have register a provider class in Jersey:

@Provider
public class MyJsonProvider extends JacksonJsonProvider {
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    @Override
    public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        ObjectMapper mapper = locateMapper(type, mediaType);
        // Enable human readable date format
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        mapper.getDeserializationConfig().setDateFormat(sdf);
        mapper.getSerializationConfig().setDateFormat(sdf);
        // Enable JAXB annotation, with Jackson annotation being the preferred one.
        AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
        AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
        AnnotationIntrospector introspector = new AnnotationIntrospector.Pair(primary, secondary);
        mapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
        mapper.getSerializationConfig().setAnnotationIntrospector(introspector);
        super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream);
    }
}

And it seems like Jersey picked it up during start up:

Jan 24, 2011 2:53:23 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Provider classes found:
  class com.mypackage.MyJsonProvider

And it works fine for serialization, but when I tried to deserialize a string like 2010-01-25 00:00:00, I'm getting a mapping error:

SEVERE: Servlet.service() for servlet JerseyWebApplication threw exception
org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.util.Date from String value '2010-01-25 00:00:00': not a valid representation (error: Can not parse date "2010-01-25 00:00:00": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
 at [Source: org.apache.catalina.connector.CoyoteInputStream@6a3d899a; line: 3, column: 37]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:159)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.weirdStringException(StdDeserializationContext.java:222)
    at org.codehaus.jackson.map.deser.StdDeserializer._parseDate(StdDeserializer.java:283)
    at org.codehaus.jackson.map.deser.DateDeserializer.deserialize(DateDeserializer.java:26)
    at org.codehaus.jackson.map.deser.DateDeserializer.deserialize(DateDeserializer.java:17)
    at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:135)
    at org.codehaus.jackson.map.deser.SettableBeanProperty$MethodProperty.deserializeAndSet(SettableBeanProperty.java:221)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:391)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:286)
    at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:1568)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:788)
    at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:398)
    at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:454)

It seems like Jersey is still using JacksonJsonProvider to handle the deserialization somehow. I'm not sure what I am doing wrong. How should I configure the jackson within jersey? Thanks!

Durandal
  • 5,575
  • 5
  • 35
  • 49
fei
  • 2,224
  • 9
  • 31
  • 36
  • 7
    I figured it out myself. Turned out I need to set the deserialization config in the readFrom() method, not the writeTo() method. Duh. – fei Jan 25 '11 at 01:04
  • 3
    Why is this closed as "too localized"? Jersey with Jackson is a common scenario. – Paul Jul 29 '13 at 23:19
  • 5
    @fei you could add it as an answer – eis Oct 08 '13 at 05:26
  • I believe Jackson will use ISO-8601 format by default (ie: `2013-04-30T07:00:00.000Z`) or numeric millis past epoch too. Text date conversion without specifying timezone can cause problems. When you have client, server and database involved, dates can get mangled very easily. – Shadow Man Feb 13 '14 at 01:48
  • Also, you might want to look at the JavaDoc for `ObjectMapper.setDateFormat`. Specifically the following: "This is preferably to directly modifying `SerializationConfig` and `DeserializationConfig` instances." – Shadow Man Feb 13 '14 at 01:53
  • I use this approach when dealing with XML: http://tinyurl.com/o26q9er, http://tinyurl.com/ojkh38h. I think XmlAdapter is logical and simple, but I don't know if Json has an equivalent, I'd like to know it myself. – zakmck Feb 17 '14 at 12:16
  • Please set this question as answered in stack overflow. Its hard to answer questions if we prowl through dozens of ones already done. – Robert Simmons Jr. Feb 25 '14 at 16:01

1 Answers1

2

super.writeTo method calls locateMapper(type, mediaType) again and creates a new instance of mapper object so the mapper object on which you configure the dateformat is never used. You can confirm in debug mode.

What you can do is create your own mapper object by creating a constructor

 MyJsonProvider(){

     ObjectMapper mapper = new ObjectMapper();
    // Enable human readable date format
    SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
    mapper.getDeserializationConfig().setDateFormat(sdf);
    mapper.getSerializationConfig().setDateFormat(sdf);
    // Enable JAXB annotation, with Jackson annotation being the preferred one.
    AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
    AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
    AnnotationIntrospector introspector = new AnnotationIntrospector.Pair(primary, secondary);
    mapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
    mapper.getSerializationConfig().setAnnotationIntrospector(introspector);
   super(mapper);}

Then you dont need to override the writeTo method

Abhijeet Kushe
  • 2,477
  • 3
  • 26
  • 39