serializza / deserializza java 8 java.time con il mappatore Jackson JSON

Come utilizzo il mappatore Jackson JSON con Java 8 LocalDateTime?

org.codehaus.jackson.map.JsonMappingException: imansible istanziare il valore di tipo [tipo semplice, class java.time.LocalDateTime] da JSON String; nessun metodo costruttore / factory a stringa singola (attraverso la catena di riferimenti: MyDTO [“campo1”] -> SubDTO [“date”])

Non è necessario utilizzare serializzatori / deserializzatori personalizzati qui. Usa il modulo datetime di jackson-modules-java8 :

Modulo datatype per far riconoscere a Jackson i tipi di dati API 8 Data & Time Java (JSR-310).

Questo modulo aggiunge il supporto per diverse classi:

  • Durata
  • Immediato
  • LocalDateTime
  • LocalDate
  • Ora locale
  • Mese giorno
  • OffsetDateTime
  • OffsetTime
  • Periodo
  • Anno
  • Anno mese
  • ZonedDateTime
  • IDArea
  • ZoneOffset

Aggiornamento: Lasciando questa risposta per ragioni storiche, ma io non lo consiglio. Si prega di consultare la risposta accettata sopra.

Dì a Jackson di mappare usando le tue classi di serializzazione [de] personalizzate:

 @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime ignoreUntil; 

fornire classi personalizzate:

 public class LocalDateTimeSerializer extends JsonSerializer { @Override public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException, JsonProcessingException { arg1.writeString(arg0.toString()); } } public class LocalDateTimeDeserializer extends JsonDeserializer { @Override public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException, JsonProcessingException { return LocalDateTime.parse(arg0.getText()); } } 

fatto casuale: se nido sopra le classi e non le rendono statiche, il messaggio di errore è strano: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported

Questa dipendenza di esperti risolverà il tuo problema:

  com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.6.5  

Una cosa che ho faticato è che per il fuso orario ZonedDateTime è stato cambiato in GMT durante la deserializzazione. È venuto fuori che, per impostazione predefinita, jackson lo sostituisce con uno dal contesto. Per mantenere la zona, è necessario disabilitare questa ‘funzione’

 Jackson2ObjectMapperBuilder.json() .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) 

Se stai usando la class ObjectMapper di fasterxml, per impostazione predefinita ObjectMapper non capisce la class LocalDateTime, quindi devi aggiungere un’altra dipendenza a gradle / maven:

 compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3' 

Ora è necessario registrare il supporto del tipo di dati offerto da questa libreria nell’object objectmapper, questo può essere fatto seguendo:

 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); 

Ora, nel tuo jsonString, puoi facilmente inserire il tuo campo java.LocalDateTime come segue:

 { "user_id": 1, "score": 9, "date_time": "2016-05-28T17:39:44.937" } 

Facendo tutto ciò, la conversione dell’object JSON in Java funzionerà correttamente, puoi leggere il file seguendo:

 objectMapper.readValue(jsonString, new TypeReference>() { }); 

Ho avuto un problema simile durante l’utilizzo di avvio di spring . Con Spring boot 1.5.1.RELEASE tutto quello che dovevo fare è aggiungere dipendenza:

  com.fasterxml.jackson.datatype jackson-datatype-jsr310  

Se si sta utilizzando Jersey, è necessario aggiungere la dipendenza Maven (jackson-datatype-jsr310) come suggerito dagli altri e registrare l’istanza del mapper object in questo modo:

 @Provider public class JacksonObjectMapper implements ContextResolver { final ObjectMapper defaultObjectMapper; public JacksonObjectMapper() { defaultObjectMapper = createDefaultMapper(); } @Override public ObjectMapper getContext(Class type) { return defaultObjectMapper; } private static ObjectMapper createDefaultMapper() { final ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); return mapper; } } 

Quando registri Jackson nelle tue risorse, devi aggiungere questo mapper in questo modo:

 final ResourceConfig rc = new ResourceConfig().packages(""); rc .register(JacksonObjectMapper.class) .register(JacksonJaxbJsonProvider.class); 

Se non è ansible utilizzare jackson-modules-java8 per qualsiasi motivo, è ansible (de-) serializzare il campo istantaneo per tutto il tempo usando @JsonIgnore e @JsonGetter & @JsonSetter :

 public class MyBean { private Instant time = Instant.now(); @JsonIgnore public Instant getTime() { return this.time; } public void setTime(Instant time) { this.time = time; } @JsonGetter private long getEpochTime() { return this.time.toEpochMilli(); } @JsonSetter private void setEpochTime(long time) { this.time = Instant.ofEpochMilli(time); } } 

Esempio:

 @Test public void testJsonTime() throws Exception { String json = new ObjectMapper().writeValueAsString(new MyBean()); System.out.println(json); MyBean myBean = new ObjectMapper().readValue(json, MyBean.class); System.out.println(myBean.getTime()); } 

i rendimenti

 {"epochTime":1506432517242} 2017-09-26T13:28:37.242Z 

Questo è solo un esempio di come usarlo in un test unitario che ho hackerato per eseguire il debug di questo problema. Gli ingredienti chiave sono

  • mapper.registerModule (nuovo JavaTimeModule ());
  • dipendenza di tipo jackson-datatype-jsr310

Codice: import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.testng.Assert; import org.testng.annotations.Test;

 import java.io.IOException; import java.io.Serializable; import java.time.Instant; class Mumu implements Serializable { private Instant from; private String text; Mumu(Instant from, String text) { this.from = from; this.text = text; } public Mumu() { } public Instant getFrom() { return from; } public String getText() { return text; } @Override public String toString() { return "Mumu{" + "from=" + from + ", text='" + text + '\'' + '}'; } } public class Scratch { @Test public void JacksonInstant() throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); Mumu before = new Mumu(Instant.now(), "before"); String jsonInString = mapper.writeValueAsString(before); System.out.println("-- BEFORE --"); System.out.println(before); System.out.println(jsonInString); Mumu after = mapper.readValue(jsonInString, Mumu.class); System.out.println("-- AFTER --"); System.out.println(after); Assert.assertEquals(after.toString(), before.toString()); } } 

Uso questo formato dell’ora: "{birthDate": "2018-05-24T13:56:13Z}" per deserializzare da json a java.time.Instant (vedi screenshot)

inserisci la descrizione dell'immagine qui

Puoi impostarlo nel tuo file application.yml per risolvere Instant time, che è l’API Date in java8:

 spring.jackson.serialization.write-dates-as-timestamps=false