Jersey + Jackson sérialisation format de date JSON - comment changer le format ou l'utilisation JacksonJsonProvider personnalisé

StackOverflow https://stackoverflow.com/questions/4428109

Question

J'utilise Jersey + Jackson pour fournir la couche de services REST JSON pour mon application. Le problème que j'ai est que le format par défaut Date de sérialisation ressemble que:

"CreationDate":1292236718456

Dans un premier temps je pensais que c'est un horodatage UNIX ... mais il est trop long pour cela. Ma bibliothèque JS côté client a des problèmes désérialisation ce format (il prend en charge un groupe de différents formats de date, mais pas celui-ci je suppose). Je souhaite modifier le format afin qu'il puisse être consommable par ma bibliothèque (ISO par exemple). Comment puis-je faire ça ... J'ai trouvé un morceau de code qui pourrait aider, mais ... où dois-je le mets que je ne contrôle pas les Jackson sérialiseur instanciation (Jersey fait)?

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

J'ai aussi trouvé ce code pour JacksonJsonProvider personnalisé - la question est .. comment puis-je fais toutes mes classes POJO utilisent

?
@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;
        }
    }
}
Était-ce utile?

La solution

Pour ce que ça vaut, ce nombre est horodatage Java standard (utilisé par les classes JDK); Unix secondes stocke, millisecondes Java, ce qui explique pourquoi sa valeur peu plus grande.

J'espère qu'il ya des documents quant à la façon d'injecter ObjectMapper dans Jersey (il devrait suivre la manière habituelle d'injecter objet fourni). Mais sinon, vous pouvez remplacer JacksonJaxRsProvider pour spécifier / configurer ObjectMapper et registre qui; c'est ce que Jersey se fait, et il y a plusieurs façons de le faire.

Autres conseils

J'ai réussi à le faire dans Resteasy « JAX-RS chemin », il devrait donc travailler sur chaque mise en œuvre conforme comme Jersey (récemment testé avec succès sur le serveur JEE7 wildfly 8, juste nécessaire quelques changements à la partie Jackson parce qu'ils changé quelques API).

Vous devez définir un ContextResolver (chèque qui produit contient le type de contenu correct):

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

}

Ensuite, vous devez retourner la classe nouvellement créée dans les getClasses de votre javax.ws.rs.core.Application

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

}

cette façon toute opération effectuée par jackson sont donnés la ObjectMapper de votre choix.

EDIT: J'ai récemment découvert à mes dépenses que l'utilisation RestEasy 2.0.1 (et donc Jackson 1.5.3) il y a un comportement étrange si vous décidez d'étendre le JacksonConfigurator ajouter les correspondances personnalisées

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

Si vous le faites comme ça (et de mettre bien sûr la classe étendue dans RestApplication) le cartographe de la classe parente est utilisée, qui vous perdez les applications personnalisées. Pour le faire fonctionner correctement, je devais faire quelque chose qui semble inutile de me autrement:

public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper> 

Pour configurer votre propre ObjectMapper, vous devez injecter votre propre classe qui implémente ContextResolver

Exactement comment obtenir le maillot de choisir ce type de up dépendra de votre CIO (printemps, Guice). J'utilise le printemps, et ma classe ressemble à quelque chose comme ceci:

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

Si vous choisissez de travailler avec Joda objets DateTime sur votre serveur et que vous voulez sérialiser ISO8601 vous pouvez utiliser JodaModule Jackson . Vous pouvez enregistrer un fournisseur de Jersey comme suit:

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<ObjectMapper> {

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

Plus d'informations disponibles sur site web de Jersey .

J'ai eu le même problème (en utilisant Jersey + Jackson + JSON), le client envoyais une date, mais il a été changé en cours dans le serveur lorsque les données ont été mises en correspondance dans l'objet.

Je suivais autre approche pour résoudre ce problème, en lisant ce lien: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html , quand je me suis aperçu que la date de réception était un TimeStamp (le même que celui Adrin dans sa question: "creationDate":1292236718456)

Dans ma classe VO J'ai ajouté cette annotation à l'@XmlJavaTypeAdapter d'attribut et également mis en œuvre une classe interne wich XmlAdapter étendue:

@XmlRootElement
public class MyClassVO {
   ...
   @XmlJavaTypeAdapter(DateFormatterAdapter.class) 
   Date creationDate;
   ...

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

J'espère que cela pourrait aider à vous aussi.

Ci-dessous le code a fonctionné pour moi - 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(<your date format>));
    DeserializationConfig deserializationConfig = objectMapper.getDeserializationConfig();
    deserializationConfig.setDateFormat(new SimpleDateFormat(<your date format>));
    objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);

  }

  public JsonProvider() {
    super.setMapper(objectMapper);
  }
}

Réécrire le MessageBodyWriter JSON avec cette

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 ) est un Java complet à / de la bibliothèque de sérialisation JSON. Lorsque vous utilisez pour écrire la chaîne JSON, vous pouvez définir comment dates sont formatées. Par défaut, les dates sont écrites aussi longtemps (comme ci-dessus, qui est millisecondes depuis le 1er janvier 1970). Cependant, vous pouvez lui donner un format chaîne ou Java DateFormatter et ont les dates écrites dans le format que vous souhaitez.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top