Jersey + Jackson JSON formato della data serializzazione - come cambiare il formato o l'uso JacksonJsonProvider personalizzato

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

Domanda

Sto usando Jersey + Jackson per fornire REST JSON livello di servizi per la mia applicazione. Il problema che ho è che il default Data di serializzazione sguardi formato del genere:

"CreationDate":1292236718456

In un primo momento ho pensato che è un timestamp di UNIX ... ma è troppo lungo per questo. Il mio lato client libreria JS ha problemi deserializzazione questo formato (supporta una serie di diversi formati di data ma non questo suppongo). Voglio cambiare il formato in modo che possa essere di consumo da parte mia biblioteca (ISO, per esempio). Come faccio a fare che ... ho trovato un pezzo di codice che potrebbe aiutare, ma ... dove lo metto come io non controllo il serializzatore instantiation Jackson (Jersey fa)?

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

Ho trovato anche questo codice per JacksonJsonProvider personalizzato - la domanda è .. come faccio a fare tutte le mie classi POJO usano

?
@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;
        }
    }
}
È stato utile?

Soluzione

Per quel che vale, quel numero è timestamp Java standard (utilizzato dalle classi JDK); Unix negozi secondi, millisecondi Java, che è il motivo per cui il suo valore po 'più grande.

Mi auguro ci sono alcuni documenti come a come iniettare ObjectMapper in Jersey (che dovrebbe seguire il solito modo per iniettare fornito oggetto). Ma in alternativa si può ignorare JacksonJaxRsProvider per specificare / configurare ObjectMapper e registrare quello; questo è ciò che fa per sé Jersey, e ci sono diversi modi per farlo.

Altri suggerimenti

Sono riuscito a farlo in Resteasy "il modo in JAX-RS", così dovrebbe funzionare su ogni applicazione compatibile come Jersey (recentemente testato con successo su server di JEE7 wildfly 8, è solo richiesto alcune modifiche alla parte di Jackson, perché cambiato alcune API).

È necessario definire una ContextResolver (controllo che produce contenga il corretto tipo di contenuto):

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

}

Poi si deve restituire la classe appena creata nel getClasses del 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;
      }

}

in questo modo tutte le operazioni effettuate tramite Jackson viene data l'ObjectMapper di vostra scelta.

EDIT: Ho recentemente scoperto a mie spese che l'uso di RESTEasy 2.0.1 (e quindi Jackson 1.5.3) v'è un comportamento strano se si decide di estendere la JacksonConfigurator per aggiungere mappature personalizzate

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

Se solo fare come questo (e, naturalmente, mettere la classe estesa in RestApplication) mapper della classe madre viene utilizzato, cioè si perde le mappature personalizzate. Per farlo funzionare correttamente che dovevo fare qualcosa che sembra inutile per me altrimenti:

public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper> 

Per configurare il proprio ObjectMapper, è necessario iniettare la propria classe che implementa ContextResolver

Esattamente come raggiungere jersey di scegliere questo tipo di up sarà dipenderà dal vostro CIO (primavera, Guice). Io uso la primavera, e la mia classe di simile a questa:

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

Se si sceglie di lavorare con Joda DateTime oggetti sul server e desidera serializzare a ISO8601 è possibile utilizzare di Jackson JodaModule . È possibile registrare un provider 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<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;
  }
}

Maggiori informazioni disponibili alla sito web di Jersey .

Ho avuto lo stesso problema (usando Jersey + Jackson + JSON), il cliente è stato l'invio di una data, ma veniva cambiato nel server quando i dati sono stati mappati in oggetto.

Ho seguito altro approccio per risolvere questo, leggendo questo link: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html , quando mi sono reso conto che le informazioni ricevute è stato un TimeStamp (lo stesso che Adrin nella sua domanda: "creationDate":1292236718456)

Nella mia classe VO ho aggiunto questa annotazione al @XmlJavaTypeAdapter attributi e anche implementato una classe interna Wich XmlAdapter estesa:

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

Spero che potrebbe aiutare anche a voi.

Di seguito il 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(<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);
  }
}

Riscriviamo il MessageBodyWriter JSON 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 ) è un Java completa per / dalla libreria serializzazione JSON. Quando si utilizza per scrivere la stringa JSON, è possibile impostare come date sono formattati. Per impostazione predefinita, le date sono scritte il più a lungo (come sopra, che è millisecondi dal 1 gennaio 1970). Tuttavia, si può dare un formato stringa o Java DateFormatter e hanno le date scritte in qualsiasi formato che si desidera.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top