Formato JSON Java 8 LocalDateTime en Spring Boot

112

Tengo un pequeño problema al formatear un Java 8 LocalDateTime en mi aplicación Spring Boot. Con fechas 'normales' no tengo ningún problema, pero los campos LocalDateTime se convierten a lo siguiente:

"startDate" : {
    "year" : 2010,
    "month" : "JANUARY",
    "dayOfMonth" : 1,
    "dayOfWeek" : "FRIDAY",
    "dayOfYear" : 1,
    "monthValue" : 1,
    "hour" : 2,
    "minute" : 2,
    "second" : 0,
    "nano" : 0,
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    }
  }

Si bien me gustaría convertirlo en algo como:

"startDate": "2015-01-01"

Mi código se ve así:

@JsonFormat(pattern="yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
public LocalDateTime getStartDate() {
    return startDate;
}

Pero cualquiera de las anotaciones anteriores no funciona, la fecha sigue formateándose como arriba. ¡Sugerencias bienvenidas!

Erik Pragt
fuente

Respuestas:

134

actualización : Spring Boot 2.x ya no requiere esta configuración. He escrito una respuesta más actualizada aquí .


(Esta es la forma de hacerlo antes de Spring Boot 2.x, podría ser útil para las personas que trabajan en una versión anterior de Spring Boot)

Finalmente encontré aquí cómo hacerlo. Para solucionarlo, necesitaba otra dependencia:

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0")

Al incluir esta dependencia, Spring registrará automáticamente un convertidor, como se describe aquí . Después de eso, debe agregar lo siguiente a application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Esto asegurará que se utilice un convertidor correcto y las fechas se imprimirán en el formato de 2016-03-16T13:56:39.492

Las anotaciones solo son necesarias en caso de que desee cambiar el formato de fecha.

Erik Pragt
fuente
24
Probablemente valga la pena incluir la siguiente anotación - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy
3
Probablemente sea mejor usar una application.propertiesentrada, como sugiere la respuesta de @patelb.
miembros alrededor del
No funciona. ¡Pero la respuesta de patelib simplemente funciona fuera de la caja!
Mykhaylo Kopytonenko
Como aviso, el nuestro necesitaba la @JsonSerializeanotación que Nick mencionó para que funcionara.
pennstatephil
92

Agregué la com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1dependencia y comencé a obtener la fecha en el siguiente formato:

"birthDate": [
    2016,
    1,
    25,
    21,
    34,
    55
  ]

que no es lo que quería pero me estaba acercando. Luego agregué lo siguiente

spring.jackson.serialization.write_dates_as_timestamps=false

al archivo application.properties que me dio el formato correcto que necesitaba.

"birthDate": "2016-01-25T21:34:55"
patelb
fuente
2
Funcionó fuera de la caja al incluir la jackson-datatype-jsr310dependencia. Esta debería ser la respuesta aceptada.
miembros alrededor del
6
Como un FYI, la parte application.properties se puede hacer a través de la configuración de Java si está configurando ObjectMapper con:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin
31

Aquí está en maven, con la propiedad para que pueda sobrevivir entre las actualizaciones de arranque de primavera

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Matt Broekhuis
fuente
1
Use esta solución con el comentario de @NickGrealy: Probablemente valga la pena incluir la siguiente anotación -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog
19

1) Dependencia

 compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.8.8' 

2) Anotación con formato de fecha y hora.

public class RestObject {

    private LocalDateTime timestamp;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public LocalDateTime getTimestamp() {
        return timestamp;
    }
}

3) Configuración de primavera.

@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        System.out.println("Config is starting.");
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}
Yan Khonski
fuente
2
Muchas gracias. @JsonFormat es el que me lo resolvió. Ninguna de las soluciones anteriores funcionó para mí por alguna razón
theprogrammer
7

Encontré otra solución que puede convertir a cualquier formato que desee y aplicar a todos los tipos de datos LocalDateTime y no tiene que especificar @JsonFormat por encima de cada tipo de datos LocalDateTime. primero agregue la dependencia:

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

Agregue el siguiente frijol:

@Configuration
public class Java8DateTimeConfiguration {
    /**
     * Customizing
     * http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
     *
     * Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).
     */
    @Bean
    public Module jsonMapperJava8DateTimeModule() {
        val bean = new SimpleModule();

        bean.addDeserializer (ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return ZonedDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
            }
        });

        bean.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return LocalDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            }
        });

        bean.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
            @Override
            public void serialize(
                    ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime));
            }
        });

        bean.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(
                    LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime));
            }
        });

        return bean;
    }
}

en su archivo de configuración agregue lo siguiente:

@Import(Java8DateTimeConfiguration.class)

Esto serializará y deserializará todas las propiedades LocalDateTime y ZonedDateTime siempre que esté utilizando objectMapper creado por Spring.

El formato que obtuvo para ZonedDateTime es: "2017-12-27T08: 55: 17.317 + 02: 00 [Asia / Jerusalem]" para LocalDateTime es: "2017-12-27T09: 05: 30.523"

Ida Amit
fuente
Probablemente necesite reemplazar val bean por SimpleModule bean = new SimpleModule ();
Abhishek Galoda
¿Esto es para Java 9?
Daniel Dai
@DanielDai Esto es en Java 8.
Ida Amit
@Abhi, SimpleModuleextiende Module. Modulees un resumen. val bean = new SimpleModule(); funciona perfecto.
Ida Amit
para SpringBoot versión 1.5.3.RELEASE no fue necesario para agregar la dependencia jackson-datatype-jsr310.
Moon13
7

Escribiendo esta respuesta como un recordatorio para mí también.

Combiné varias respuestas aquí y al final la mía funcionó con algo como esto. (Estoy usando SpringBoot 1.5.7 y Lombok 1.16.16)

@Data
public Class someClass {

   @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
   @JsonSerialize(using = LocalDateTimeSerializer.class)
   @JsonDeserialize(using = LocalDateTimeDeserializer.class)
   private LocalDateTime someDate;

}
JWiryo
fuente
1
es posible que desee agregar importaciones para DateTimeFormaty otros.
prayagupd
4

Esto funciona bien:

Agrega la dependencia:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

Agrega la anotación:

@JsonFormat(pattern="yyyy-MM-dd")

Ahora, debe obtener el formato correcto.

Para usar el mapeador de objetos, necesita registrar JavaTime

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Manuel Antonio
fuente
1

Como ya se mencionó, spring-boot buscará todo lo que necesita (tanto para web como para webflux starter).

Pero lo que es aún mejor: no es necesario que registre ningún módulo usted mismo. Echa un vistazo aquí . Dado que se @SpringBootApplicationusa @EnableAutoConfigurationbajo el capó, significa JacksonAutoConfigurationque se agregará al contexto automáticamente. Ahora, si miras dentro JacksonAutoConfiguration, verás:

    private void configureModules(Jackson2ObjectMapperBuilder builder) {
        Collection<Module> moduleBeans = getBeans(this.applicationContext,
                Module.class);
        builder.modulesToInstall(moduleBeans.toArray(new Module[0]));
    }

Este tipo será llamado en el proceso de inicialización y buscará todos los módulos que pueda encontrar en el classpath. (Yo uso Spring Boot 2.1)

yuranos
fuente
1

Estoy usando Springboot 2.0.6 y, por alguna razón, los cambios de yml de la aplicación no funcionaron. Y también tenía más requisitos.

Intenté crear ObjectMapper y marcarlo como principal, pero Spring Boot se quejó de que ya tengo jacksonObjectMapper como principal.

Entonces esto es lo que hice. Hice cambios en el mapeador interno.

Mi serializador y deserializador son especiales: se ocupan de 'dd / MM / AAAA'; y mientras deserializa, hace todo lo posible por usar 3-4 formatos populares para asegurarse de que tengo algo de LocalDate.

@Autowired
ObjectMapper mapper;

@PostConstruct
public ObjectMapper configureMapper() {
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

    mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    module.addSerializer(LocalDate.class, new LocalDateSerializer());
    mapper.registerModule(module);

    return mapper;
}
Kalpesh Soni
fuente
0

@JsonDeserialize(using= LocalDateDeserializer.class) no me funciona con la siguiente dependencia.

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

He utilizado el siguiente convertidor de código para deserializar la fecha en un archivo java.sql.Date.

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;


@SuppressWarnings("UnusedDeclaration")
@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<java.time.LocalDate, java.sql.Date> {


    @Override
    public java.sql.Date convertToDatabaseColumn(java.time.LocalDate attribute) {

        return attribute == null ? null : java.sql.Date.valueOf(attribute);
    }

    @Override
    public java.time.LocalDate convertToEntityAttribute(java.sql.Date dbData) {

        return dbData == null ? null : dbData.toLocalDate();
    }
}
RAM
fuente
0

simplemente usa:

@JsonFormat(pattern="10/04/2019")

o puede usar el patrón que desee, por ejemplo: ('-' in place of '/')

Ashish Dwivedi
fuente
Esta respuesta se ha compartido antes, pero luego con la sintaxis correcta.
Erik Pragt
0

Estoy usando Spring Boot 2.1.8. Yo he importado

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

que incluye jackson-datatype-jsr310 .

Luego, tuve que agregar estas anotaciones

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("date")
LocalDateTime getDate();

y funciona. El JSON se ve así:

"date": "2020-03-09 17:55:00"
Tomas Lukac
fuente
Sugerencia: incluir spring-boot-starter-jsonsolo es necesario si spring-boot-starter-webfalta.
Judomu