jackson - parse - localdate serializer
serializar/deserializar java 8 java.time con el mapeador Jackson JSON (14)
¿Cómo uso el mapeador Jackson JSON con Java 8 LocalDateTime?
org.codehaus.jackson.map.JsonMappingException: no se puede crear una instancia del valor de tipo [tipo simple, clase java.time.LocalDateTime] de la cadena JSON; sin constructor de cadena única / método de fábrica (a través de la cadena de referencia: MyDTO ["field1"] -> SubDTO ["date"])
Actualización: Dejando esta respuesta por razones históricas, pero no la recomiendo. Por favor vea la respuesta aceptada arriba.
Dígale a Jackson que asigne usando sus clases de serialización [de] personalizadas:
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;
proporcionar clases personalizadas:
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
arg1.writeString(arg0.toString());
}
}
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
return LocalDateTime.parse(arg0.getText());
}
}
hecho aleatorio: si
org.springframework.web.HttpMediaTypeNotSupportedException: Content type ''application/json;charset=UTF-8'' not supported
clases anteriores y no las hago estáticas, el mensaje de error es extraño:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type ''application/json;charset=UTF-8'' not supported
Esta dependencia de Maven resolverá su problema:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
Una cosa que me ha costado es que la zona horaria ZonedDateTime se cambie a GMT durante la deserialización. Resultó que, por defecto, Jackson lo reemplaza con uno del contexto. Para mantener la zona, uno debe deshabilitar esta ''característica''
Jackson2ObjectMapperBuilder.json()
.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
Este es solo un ejemplo de cómo usarlo en una prueba unitaria que pirateé para depurar este problema. Los ingredientes clave son
-
mapper.registerModule(new JavaTimeModule());
-
dependencia maven de
<artifactId>jackson-datatype-jsr310</artifactId>
Código:
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());
}
}
No es necesario usar serializadores / deserializadores personalizados aquí. Utilice el módulo de fecha y hora de jackson-modules-java8 :
Módulo de tipo de datos para que Jackson reconozca los tipos de datos de la API de fecha y hora Java 8 (JSR-310).
Este módulo agrega soporte para bastantes clases:
- Duración
- Instante
- LocalDateTime
- Fecha local
- Hora local
- Mes dia
- OffsetDateTime
- OffsetTime
- Período
- Año
- Año mes
- ZonedDateTime
- ZoneId
- ZoneOffset
Para aquellos que usan Spring Boot 2.x
No es necesario hacer nada de lo anterior: Java 8 LocalDateTime está serializado / deserializado de fábrica. Tuve que hacer todo lo anterior en 1.x, pero con Boot 2.x, funciona a la perfección.
Consulte también esta referencia JSON Java 8 LocalDateTime format en Spring Boot
Puede configurar esto en su archivo
application.yml
para resolver la hora instantánea, que es la API de fecha en java8:
spring.jackson.serialization.write-dates-as-timestamps=false
Si alguien tiene problemas al usar
SpringBoot
aquí es cómo solucioné el problema sin agregar una nueva dependencia.
En la
Spring 2.1.3
Jackson espera que la cadena de fecha
2019-05-21T07:37:11.000
en este formato
yyyy-MM-dd HH:mm:ss.SSS
des-
LocalDateTime
en
LocalDateTime
.
Asegúrese de que la cadena de fecha separe la fecha y la hora con
T
no con el
space
.
segundos (
ss
) y milisegundos (
SSS
) podrían omitirse.
@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;
Si considera usar fastjson, puede resolver su problema, tenga en cuenta la versión
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
Si está utilizando Jersey, entonces necesita agregar la dependencia Maven (jackson-datatype-jsr310) como lo sugirieron los demás y registrar su instancia de mapeador de objetos de la siguiente manera:
@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {
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;
}
}
Al registrar a Jackson en sus recursos, debe agregar este mapeador de la siguiente manera:
final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
.register(JacksonObjectMapper.class)
.register(JacksonJaxbJsonProvider.class);
Si está utilizando Spring Boot y tiene este problema con el OffsetDateTime, entonces necesita usar el registerModules como respondió anteriormente @greperror (respondió el 28 de mayo de 16 a las 13:04) pero tenga en cuenta que hay una diferencia. La dependencia mencionada no necesita ser agregada ya que supongo que Spring Boot ya la tiene. Estaba teniendo este problema con Spring boot y funcionó para mí sin agregar esta dependencia.
Si está utilizando la clase ObjectMapper de rapidxml, por defecto ObjectMapper no entiende la clase LocalDateTime, por lo tanto, debe agregar otra dependencia en su gradle / maven:
compile ''com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3''
Ahora debe registrar el soporte de tipo de datos que ofrece esta biblioteca en su objeto objectmapper, esto se puede hacer de la siguiente manera:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
Ahora, en su jsonString, puede poner fácilmente su campo java.LocalDateTime de la siguiente manera:
{
"user_id": 1,
"score": 9,
"date_time": "2016-05-28T17:39:44.937"
}
Al hacer todo esto, su conversión de archivo Json a objeto Java funcionará bien, puede leer el archivo de la siguiente manera:
objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
});
Si no puede usar
jackson-modules-java8
por cualquier razón, puede (des) serializar el campo instantáneo
long
use
@JsonIgnore
y
@JsonGetter
y
@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);
}
}
Ejemplo:
@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());
}
rendimientos
{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z
Tuve un problema similar al usar Spring Boot . Con Spring boot 1.5.1.RELEASE todo lo que tuve que hacer es agregar dependencia:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>