Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey + Jackson JSON date format serialization - how to change the format or use custom JacksonJsonProvider

I am using Jersey + Jackson to provide REST JSON services layer for my application. The problem I have is that the default Date serialization format looks like that:

"CreationDate":1292236718456 

At first I thought it is a UNIX timestamp... but it is too long for that. My client-side JS library has problems deserializing this format (it supports a bunch of different date formats but not this one I suppose). I want to change the format so that it can be consumable by my library (to ISO for example). How do I do that... I found a piece of code that could help but... where do I put it as I don't control the Jackson serializer instantiation (Jersey does)?

objectMapper.configure(     SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); 

I also found this code for custom JacksonJsonProvider - the question is .. how do I make all my POJO classes use it?

@Provider public class MessageBodyWriterJSON extends JacksonJsonProvider {      private static final String DF = "yyyy-MM-dd’T'HH:mm:ss.SSSZ";      @Override     public boolean isWriteable(Class arg0, Type arg1, Annotation[] arg2,             MediaType arg3) {         return super.isWriteable(arg0, arg1, arg2,                 arg3);     }     @Override     public void writeTo(Object target, Class arg1, Type arg2, Annotation[] arg3,             MediaType arg4, MultivaluedMap arg5, OutputStream outputStream)             throws IOException, WebApplicationException {             SimpleDateFormat sdf=new SimpleDateFormat(DF);          ObjectMapper om = new ObjectMapper();         om.getDeserializationConfig().setDateFormat(sdf);         om.getSerializationConfig().setDateFormat(sdf);         try {             om.writeValue(outputStream, target);         } catch (JsonGenerationException e) {             throw e;         } catch (JsonMappingException e) {             throw e;         } catch (IOException e) {             throw e;         }     } } 
like image 492
adrin Avatar asked Dec 13 '10 10:12

adrin


People also ask

How do I change the date format in object mapper?

We can format a date using the setDateFormat() of ObjectMapper class. This method can be used for configuring the default DateFormat when serializing time values as Strings and deserializing from JSON Strings.

What is the default date format in Jackson?

It's important to note that Jackson will serialize the Date to a timestamp format by default (number of milliseconds since January 1st, 1970, UTC).


1 Answers

I managed to do it in Resteasy "the JAX-RS way", so it should work on every compliant implementation like Jersey (recently successfully tested on JEE7 server Wildfly 8, it just required a few changes to the Jackson part because they changed a few APIs).

You must define a ContextResolver (check that Produces contains the correct content-type):

import javax.ws.rs.ext.ContextResolver; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.map.DeserializationConfig; import javax.ws.rs.ext.Provider; import javax.ws.rs.Produces;  import java.text.SimpleDateFormat; @Provider @Produces("application/json") public class JacksonConfigurator implements ContextResolver<ObjectMapper> {      private ObjectMapper mapper = new ObjectMapper();      public JacksonConfigurator() {         SerializationConfig serConfig = mapper.getSerializationConfig();         serConfig.setDateFormat(new SimpleDateFormat(<my format>));         DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();         deserializationConfig.setDateFormat(new SimpleDateFormat(<my format>));         mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);     }      @Override     public ObjectMapper getContext(Class<?> arg0) {         return mapper;     }  } 

Then you must return the newly created class in your javax.ws.rs.core.Application's getClasses

import javax.ws.rs.core.Application; public class RestApplication extends Application {       @Override      public Set<Class<?>> getClasses() {          Set<Class<?>> classes = new HashSet<Class<?>>();          // your classes here          classes.add(JacksonConfigurator.class);          return classes;       }  } 

this way all operation made through jackson are given the ObjectMapper of your choice.

EDIT: I recently found out at my expenses that using RestEasy 2.0.1 (and thus Jackson 1.5.3) there is a strange behaviour if you decide to extend the JacksonConfigurator to add custom mappings.

import javax.ws.rs.core.MediaType; @Provider @Produces(MediaType.APPLICATION_JSON) public class MyJacksonConfigurator extends JacksonConfigurator 

If you just do like this (and of course put the extended class in RestApplication) the mapper of the parent class is used, that is you lose the custom mappings. To make it correctly work I had to do something that seems useless to me otherwise:

public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper>  
like image 95
Riccardo Cossu Avatar answered Oct 12 '22 19:10

Riccardo Cossu