parse not localdatetime exist error creators cannot java json jackson jax-rs resteasy

java - not - localdatetime jackson serializer



Formato Java 8 LocalDate Jackson (9)

Para java.util.Date cuando lo hago

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy") private Date dateOfBirth;

luego en la solicitud JSON cuando envío

{ {"dateOfBirth":"01/01/2000"} }

funciona.

¿Cómo debo hacer esto para el campo LocalDate de Java 8 ?

Traté de tener

@JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate dateOfBirth;

No funcionó.

¿Puede alguien decirme cuál es la forma correcta de hacer esto?

Debajo están las dependencias

<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>jaxrs-api</artifactId> <version>3.0.9.Final</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> <version>2.4.2</version> </dependency> <dependency> <groupId>com.wordnik</groupId> <artifactId>swagger-annotations</artifactId> <version>1.3.10</version> </dependency> <dependency>


@JsonSerialize y @JsonDeserialize funcionaron bien para mí. Eliminan la necesidad de importar el módulo adicional jsr310:

@JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate dateOfBirth;

Deserializador:

public class LocalDateDeserializer extends StdDeserializer<LocalDate> { private static final long serialVersionUID = 1L; protected LocalDateDeserializer() { super(LocalDate.class); } @Override public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return LocalDate.parse(jp.readValueAs(String.class)); } }

Serializador:

public class LocalDateSerializer extends StdSerializer<LocalDate> { private static final long serialVersionUID = 1L; public LocalDateSerializer(){ super(LocalDate.class); } @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException { gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE)); } }


Dado que LocalDateSerializer convierte en "[año, mes, día]" (una matriz json) en lugar de "año-mes-día" (una cadena json) de forma predeterminada, y dado que no quiero requerir ninguna configuración especial de ObjectMapper ( puede hacer que LocalDateSerializer genere cadenas si deshabilita SerializationFeature.WRITE_DATES_AS_TIMESTAMPS pero eso requiere una configuración adicional en su ObjectMapper ), utilizo lo siguiente:

importaciones:

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

código:

// generates "yyyy-MM-dd" output @JsonSerialize(using = ToStringSerializer.class) // handles "yyyy-MM-dd" input just fine (note: "yyyy-M-d" format will not work) @JsonDeserialize(using = LocalDateDeserializer.class) private LocalDate localDate;

Y ahora puedo usar el new ObjectMapper() para leer y escribir mis objetos sin ninguna configuración especial.


En la aplicación web de arranque de primavera, con ''jackson'' y ''jsr310'' versión "2.8.5"

compile "com.fasterxml.jackson.core:jackson-databind:2.8.5" runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.5"

El ''@JsonFormat'' funciona:

import com.fasterxml.jackson.annotation.JsonFormat; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate birthDate;


En la clase de configuración, defina las clases LocalDateSerializer y LocalDateDeserializer y regístrelas en ObjectMapper a través de JavaTimeModule como se muestra a continuación:

@Configuration public class AppConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_EMPTY); //other mapper configs // Customize de-serialization JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer()); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer()); mapper.registerModule(javaTimeModule); return mapper; } public class LocalDateSerializer extends JsonSerializer<LocalDate> { @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(value.format(Constant.DATE_TIME_FORMATTER)); } } public class LocalDateDeserializer extends JsonDeserializer<LocalDate> { @Override public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return LocalDate.parse(p.getValueAsString(), Constant.DATE_TIME_FORMATTER); } } }


La solución más simple (que también admite la deserialización y la serialización) es

import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy") @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate dateOfBirth;

Mientras usa las siguientes dependencias en su proyecto.

Maven

<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.7</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.7</version> </dependency>

Gradle

compile "com.fasterxml.jackson.core:jackson-databind:2.9.7" compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"

No se requiere una implementación adicional de ContextResolver, Serializer o Deserializer.


Nunca pude hacer que esto funcione de manera simple usando anotaciones. Para que funcione, creé un ContextResolver para ObjectMapper , luego agregué el JSR310Module , junto con una advertencia más, que era la necesidad de establecer write-date-as-timestamp en false. Vea más en la documentación para el módulo JSR310 . Aquí hay un ejemplo de lo que usé.

Dependencia

<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.4.0</version> </dependency>

Nota: Un problema que enfrenté con esto es que la versión de jackson-annotation introducida por otra dependencia, utilizó la versión 2.3.2, que canceló la 2.4 requerida por el jsr310 . Lo que sucedió fue que obtuve un NoClassDefFound para ObjectIdResolver , que es una clase 2.4. Así que solo necesitaba alinear las versiones de dependencia incluidas

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JSR310Module; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; @Provider public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { private final ObjectMapper MAPPER; public ObjectMapperContextResolver() { MAPPER = new ObjectMapper(); // Now you should use JavaTimeModule instead MAPPER.registerModule(new JSR310Module()); MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); } @Override public ObjectMapper getContext(Class<?> type) { return MAPPER; } }

Clase de recursos

@Path("person") public class LocalDateResource { @GET @Produces(MediaType.APPLICATION_JSON) public Response getPerson() { Person person = new Person(); person.birthDate = LocalDate.now(); return Response.ok(person).build(); } @POST @Consumes(MediaType.APPLICATION_JSON) public Response createPerson(Person person) { return Response.ok( DateTimeFormatter.ISO_DATE.format(person.birthDate)).build(); } public static class Person { public LocalDate birthDate; } }

Prueba

curl -v http://localhost:8080/api/person
Resultado: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{/"birthDate/":/"2015-03-01/"}" http://localhost:8080/api/person
Resultado: 2015-03-01

Consulte también here solución JAXB.

ACTUALIZAR

El JSR310Module está en desuso a partir de la versión 2.7 de Jackson. En su lugar, debe registrar el módulo JavaTimeModule . Sigue siendo la misma dependencia.


Solo una actualización de la respuesta de Christopher.

Desde la versión 2.6.0

<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.0</version> </dependency>

Utilice JavaTimeModule en lugar de JSR310Module (en desuso).

@Provider public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { private final ObjectMapper MAPPER; public ObjectMapperContextResolver() { MAPPER = new ObjectMapper(); MAPPER.registerModule(new JavaTimeModule()); MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); } @Override public ObjectMapper getContext(Class<?> type) { return MAPPER; } }

De acuerdo con la documentation , el nuevo JavaTimeModule usa la misma configuración estándar para la serialización predeterminada que NO usa Id. De zona horaria, y en su lugar solo utiliza compensaciones de zona horaria compatibles con ISO-8601.

El comportamiento se puede cambiar usando SerializationFeature.WRITE_DATES_WITH_ZONE_ID


https://.com/a/53251526/1282532 es la forma más simple de serializar / deserializar la propiedad. Tengo dos preocupaciones con respecto a este enfoque: hasta cierto punto, violación del principio DRY y alto acoplamiento entre pojo y mapper.

public class Trade { @JsonFormat(pattern = "yyyyMMdd") @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate tradeDate; @JsonFormat(pattern = "yyyyMMdd") @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate maturityDate; @JsonFormat(pattern = "yyyyMMdd") @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) private LocalDate entryDate; }

En caso de que tenga POJO con múltiples campos LocalDate, es mejor configurar el mapeador en lugar de POJO. Puede ser tan simple como https://.com/a/35062824/1282532 si está utilizando valores ISO-8601 ("2019-01-31")

En caso de que necesite manejar un formato personalizado, el código será así:

ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyyMMdd"))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd"))); mapper.registerModule(javaTimeModule);

La lógica se escribe solo una vez, se puede reutilizar para múltiples POJO


ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

funciona bien para mi