Formato JSON Java 8 LocalDateTime in Spring Boot


112

Ho un piccolo problema con la formattazione di un LocalDateTime di Java 8 nella mia applicazione Spring Boot. Con le date 'normali' non ho problemi, ma i campi LocalDateTime vengono convertiti nel seguente:

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

Mentre vorrei convertirlo in qualcosa di simile:

"startDate": "2015-01-01"

Il mio codice ha questo aspetto:

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

Ma nessuna delle annotazioni precedenti non funziona, la data continua a essere formattata come sopra. Suggerimenti benvenuti!

Risposte:


135

aggiornamento : Spring Boot 2.x non richiede più questa configurazione. Ho scritto una risposta più aggiornata qui .


(Questo è il modo di farlo prima di Spring Boot 2.x, potrebbe essere utile per le persone che lavorano su una versione precedente di Spring Boot)

Finalmente ho trovato qui come farlo. Per risolverlo, avevo bisogno di un'altra dipendenza:

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

Includendo questa dipendenza, Spring registrerà automaticamente un convertitore per essa, come descritto qui . Successivamente, è necessario aggiungere quanto segue a application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Ciò garantirà che venga utilizzato un convertitore corretto e le date verranno stampate nel formato 2016-03-16T13:56:39.492

Le annotazioni sono necessarie solo nel caso in cui desideri modificare il formato della data.


24
Probabilmente vale la pena includere la seguente annotazione - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy

3
Probabilmente è meglio usare solo una application.propertiesvoce, come suggerito dalla risposta di @patelb.
membri suonano il

Non funziona. Ma la risposta di patelib funziona appena fuori dagli schemi!
Mykhaylo Kopytonenko

Come avvertimento, il nostro aveva bisogno @JsonSerializedell'annotazione menzionata da Nick per farlo funzionare.
pennstatephil

92

Ho aggiunto la com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1dipendenza e ho iniziato a ottenere la data nel seguente formato:

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

che non è quello che volevo ma mi stavo avvicinando. Ho quindi aggiunto quanto segue

spring.jackson.serialization.write_dates_as_timestamps=false

al file application.properties che mi ha fornito il formato corretto di cui avevo bisogno.

"birthDate": "2016-01-25T21:34:55"

2
Ha funzionato fuori dagli schemi quando si include la jackson-datatype-jsr310dipendenza. Questa dovrebbe essere la risposta accettata.
membri suonano il

6
Per tua informazione, la parte application.properties può essere eseguita tramite Java config se stai configurando ObjectMapper con:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin

31

Qui è in Maven, con la proprietà in modo da poter sopravvivere tra gli aggiornamenti dello stivale primaverile

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>

1
Usa questa soluzione con il commento @NickGrealy: Probabilmente vale la pena includere la seguente annotazione -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog

19

1) Dipendenza

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

2) Annotazione con formato data-ora.

public class RestObject {

    private LocalDateTime timestamp;

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

3) Spring Config.

@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
Grazie mille. @ JsonFormat è quello che mi ha risolto. Nessuna delle soluzioni di cui sopra ha funzionato per me per qualche motivo
programmatore

7

Ho trovato un'altra soluzione che puoi convertire in qualsiasi formato tu voglia e applicare a tutti i tipi di dati LocalDateTime e non devi specificare @JsonFormat sopra ogni tipo di dati LocalDateTime. prima aggiungi la dipendenza:

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

Aggiungi il seguente fagiolo:

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

nel tuo file di configurazione aggiungi quanto segue:

@Import(Java8DateTimeConfiguration.class)

Questo serializzerà e deserializzerà tutte le proprietà LocalDateTime e ZonedDateTime fintanto che si utilizza objectMapper creato dalla primavera.

Il formato che hai ottenuto per ZonedDateTime è: "2017-12-27T08: 55: 17.317 + 02: 00 [Asia / Jerusalem]" per LocalDateTime è: "2017-12-27T09: 05: 30.523"


Probabilmente è necessario sostituire val bean in SimpleModule bean = new SimpleModule ();
Abhishek Galoda

È questo per Java 9?
Daniel Dai

@DanielDai Questo è in Java 8.
Ida Amit

@Abhi, si SimpleModuleestende Module. Moduleè un abstract. val bean = new SimpleModule(); funziona perfettamente.
Ida Amit

per SpringBoot versione 1.5.3.RELEASE non era necessario aggiungere la dipendenza jackson-datatype-jsr310.
Moon13

7

Scrivendo questa risposta anche come promemoria per me.

Ho combinato diverse risposte qui e alla fine la mia ha funzionato con qualcosa di simile. (Sto usando SpringBoot 1.5.7 e 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
potresti voler aggiungere importazioni per DateTimeFormate altri.
Prayagupd

4

Questo funziona bene:

Aggiungi la dipendenza:

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

Aggiungi l'annotazione:

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

Ora devi ottenere il formato corretto.

Per utilizzare object mapper, è necessario registrare JavaTime

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

1

Come già accennato, spring-boot prenderà tutto ciò di cui hai bisogno (sia per web che per webflux starter).

Ma ciò che è ancora meglio: non è necessario registrare alcun modulo da soli. Dai un'occhiata qui . Poiché @SpringBootApplicationutilizza @EnableAutoConfigurationsotto il cofano, significa JacksonAutoConfigurationche verrà aggiunto automaticamente al contesto. Ora, se guardi dentro JacksonAutoConfiguration, vedrai:

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

Questo tizio verrà chiamato durante il processo di inizializzazione e recupererà tutti i moduli che può trovare nel classpath. (Io uso Spring Boot 2.1)


1

Sto usando Springboot 2.0.6 e per qualche motivo le modifiche yml dell'app non hanno funzionato. E avevo anche più requisiti.

Ho provato a creare ObjectMapper e contrassegnarlo come Primary, ma lo Spring Boot si è lamentato del fatto che jacksonObjectMapper è già contrassegnato come Primary !!

Quindi questo è quello che ho fatto. Ho apportato modifiche al mappatore interno.

I miei Serializer e Deserializer sono speciali: trattano "dd / MM / YYYY"; e durante la de-serializzazione, fa del suo meglio per usare 3-4 formati popolari per assicurarsi di avere un po 'di 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) non funziona per me con la seguente dipendenza.

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

Ho usato il convertitore di codice seguente per deserializzare la data in un file 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

usa semplicemente:

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

oppure puoi usare il pattern come preferisci per esempio: ('-' in place of '/')


Questa risposta è stata condivisa prima, ma poi con la sintassi corretta.
Erik Pragt

0

Sto usando Spring Boot 2.1.8. Ho importato

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

che include il jackson-datatype-jsr310 .

Quindi, ho dovuto aggiungere queste annotazioni

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

e funziona. Il JSON ha questo aspetto:

"date": "2020-03-09 17:55:00"

Suggerimento: l'inclusione spring-boot-starter-jsonè necessaria solo se spring-boot-starter-webmanca.
judomu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.