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!

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.


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"

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>

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

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"


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;

}

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());

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)


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

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

0

simplemente usa:

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

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


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"

Sugerencia: incluir spring-boot-starter-jsonsolo es necesario si spring-boot-starter-webfalta.
Judomu
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.