Serializzazione del formato delle date Jersey + Jackson JSON – come modificare il formato o utilizzare il JacksonJsonProvider personalizzato

Sto usando Jersey + Jackson per fornire il livello dei servizi JSON REST per la mia applicazione. Il problema che ho è che il formato di serializzazione della data di default è così:

"CreationDate":1292236718456 

All’inizio ho pensato che fosse un timestamp UNIX … ma è troppo lungo per quello. La mia libreria JS sul lato client ha problemi di deserializzazione di questo formato (supporta diversi formati di data ma non questo suppongo). Voglio cambiare il formato in modo che possa essere consumabile dalla mia libreria (ad esempio ISO). Come faccio a farlo … Ho trovato un pezzo di codice che potrebbe essere d’aiuto ma … dove lo metto perché non controllo l’istanza del serializzatore Jackson (Jersey)?

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

Ho anche trovato questo codice per JacksonJsonProvider personalizzato – la domanda è .. come faccio a far sì che tutte le mie classi POJO lo usino?

 @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; } } } 

Per quel che vale, quel numero è il timestamp standard di Java (usato dalle classi JDK); Unix archivia secondi, millisecondi Java, motivo per cui è un valore leggermente più grande.

Spero che ci siano alcuni documenti su come iniettare ObjectMapper in Jersey (dovrebbe seguire il solito modo di iniettare l’object fornito). In alternativa, è ansible sovrascrivere JacksonJaxRsProvider per specificare / configurare ObjectMapper e registrarlo; questo è quello che fa Jersey, e ci sono molti modi per farlo.

Sono riuscito a farlo in Resteasy “il modo JAX-RS”, quindi dovrebbe funzionare su tutte le implementazioni compliant come Jersey (recentemente testato con successo sul server JEE7 Wildfly 8, ha richiesto solo alcune modifiche alla parte di Jackson perché hanno cambiato alcuni API).

È necessario definire un ContextResolver (verificare che Produces contenga il tipo di contenuto corretto):

 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 { private ObjectMapper mapper = new ObjectMapper(); public JacksonConfigurator() { SerializationConfig serConfig = mapper.getSerializationConfig(); serConfig.setDateFormat(new SimpleDateFormat()); DeserializationConfig deserializationConfig = mapper.getDeserializationConfig(); deserializationConfig.setDateFormat(new SimpleDateFormat()); mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); } @Override public ObjectMapper getContext(Class arg0) { return mapper; } } 

Quindi devi restituire la class appena creata nei tuoi getClasses javax.ws.rs.core.Application

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

in questo modo tutte le operazioni effettuate tramite Jackson vengono fornite con l’ObjectMapper di tua scelta.

EDIT: Recentemente ho scoperto a mie spese che usando RestEasy 2.0.1 (e quindi Jackson 1.5.3) c’è un comportamento strano se si decide di estendere il JacksonConfigurator per aggiungere mappature personalizzate.

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

Se fai così (e ovviamente inserisci la class estesa in RestApplication) viene utilizzato il mapper della class genitore, cioè perdi i mapping personalizzati. Per farlo funzionare correttamente ho dovuto fare qualcosa che a me sembra inutile:

 public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver 

Per configurare il tuo ObjectMapper, devi inserire la tua class che implementa ContextResolver

Esattamente come ottenere la maglia per prenderlo dipende dal tuo IOC (spring, guai). Io uso la spring e la mia class assomiglia a qualcosa del genere:

 import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig.Feature; import org.codehaus.jackson.map.deser.CustomDeserializerFactory; import org.codehaus.jackson.map.deser.StdDeserializerProvider; import org.codehaus.jackson.map.ser.CustomSerializerFactory; import org.springframework.stereotype.Component; // tell spring to look for this. @Component // tell spring it's a provider (type is determined by the implements) @Provider public class ObjectMapperProvider implements ContextResolver { @Override public ObjectMapper getContext(Class type) { // create the objectMapper. ObjectMapper objectMapper = new ObjectMapper(); // configure the object mapper here, eg. objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); return objectMapper; } } 

Ho avuto lo stesso problema (usando Jersey + Jackson + Json), il client stava inviando una data, ma era stata modificata nel server quando i dati sono stati mappati nell’object.

Ho seguito un altro approccio per risolvere questo problema, leggendo questo link: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html , quando ho capito che la data ricevuta era un TimeStamp (lo stesso come Adrin nella sua domanda: "creationDate":1292236718456 )

Nella mia class VO ho aggiunto questa annotazione all’attributo @XmlJavaTypeAdapter e implementato anche una class interna con estensione XmlAdapter :

 @XmlRootElement public class MyClassVO { ... @XmlJavaTypeAdapter(DateFormatterAdapter.class) Date creationDate; ... private static class DateFormatterAdapter extends XmlAdapter { @Override public Date unmarshal(final String v) throws Exception { Timestamp stamp = new Timestamp(new Long(v)); Date date = new Date(stamp.getTime()); return date; } } 

Spero possa aiutarti anche tu.

Se scegli di lavorare con oggetti Joda DateTime sul tuo server e vuoi serializzare su ISO8601 puoi usare il JodaModule di Jackson . Puoi registrare un fornitore di Jersey come segue:

 import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.joda.JodaModule; @Provider public class MyObjectMapperProvider implements ContextResolver { final ObjectMapper objectMapper; public MyObjectMapperProvider() { objectMapper = new ObjectMapper(); /* Register JodaModule to handle Joda DateTime Objects. */ objectMapper.registerModule(new JodaModule()); /* We want dates to be treated as ISO8601 not timestamps. */ objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); } @Override public ObjectMapper getContext(Class arg0) { return objectMapper; } } 

Maggiori informazioni disponibili sul sito Web di Jersey .

Sotto codice ha funzionato per me – JAX-RS 1.1, Jersy 1.8

 import java.text.SimpleDateFormat; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.Provider; import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; @Provider @Produces(MediaType.APPLICATION_JSON) public class JsonProvider extends JacksonJaxbJsonProvider { private static final ObjectMapper objectMapper = new ObjectMapper(); static { // allow only non-null fields to be serialized objectMapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL); SerializationConfig serConfig = objectMapper.getSerializationConfig(); serConfig.setDateFormat(new SimpleDateFormat()); DeserializationConfig deserializationConfig = objectMapper.getDeserializationConfig(); deserializationConfig.setDateFormat(new SimpleDateFormat()); objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); } public JsonProvider() { super.setMapper(objectMapper); } } 

Riscrivi MessageBodyWriterJSON con questo

 import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.Provider; import org.codehaus.jackson.jaxrs.JacksonJsonProvider; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; @Provider public class MessageBodyWriterJSON extends JacksonJsonProvider { public MessageBodyWriterJSON (){ } @Override public ObjectMapper locateMapper(Class type, MediaType mediaType) { ObjectMapper mapper = super.locateMapper(type, mediaType); //DateTime in ISO format "2012-04-07T17:00:00.000+0000" instead of 'long' format mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); return mapper; } } 

json-io ( https://github.com/jdereg/json-io ) è una libreria di serializzazione Java da / a completa. Quando lo si utilizza per scrivere la stringa JSON, è ansible impostare la modalità di formattazione delle date. Per impostazione predefinita, le date sono scritte come lunghe (come sopra, che è millisecondi dal 1 gennaio 1970). Tuttavia, puoi dargli un formato String o Java DateFormatter e avere le date scritte in qualsiasi formato desideri.