Format JSON Java 8 LocalDateTime dans Spring Boot

112

J'ai un petit problème avec le formatage d'un Java 8 LocalDateTime dans mon application Spring Boot. Avec les dates `` normales '', je n'ai aucun problème, mais les champs LocalDateTime sont convertis comme suit:

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

Alors que je voudrais le convertir en quelque chose comme:

"startDate": "2015-01-01"

Mon code ressemble à ceci:

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

Mais l'une ou l'autre des annotations ci-dessus ne fonctionne pas, la date continue à être formatée comme ci-dessus. Suggestions bienvenues!

Erik Pragt
la source

Réponses:

135

mise à jour : Spring Boot 2.x ne nécessite plus cette configuration. J'ai écrit une réponse plus à jour ici .


(C'est la façon de le faire avant Spring Boot 2.x, cela pourrait être utile pour les personnes travaillant sur une ancienne version de Spring Boot)

J'ai enfin trouvé ici comment faire. Pour y remédier, j'avais besoin d'une autre dépendance:

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

En incluant cette dépendance, Spring enregistrera automatiquement un convertisseur pour celle-ci, comme décrit ici . Après cela, vous devez ajouter les éléments suivants à application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Cela garantira qu'un convertisseur correct est utilisé et les dates seront imprimées au format 2016-03-16T13:56:39.492

Les annotations ne sont nécessaires que si vous souhaitez modifier le format de la date.

Erik Pragt
la source
24
Cela vaut probablement la peine d'inclure l'annotation suivante - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy
3
Il est probablement préférable d'utiliser simplement une application.propertiesentrée, comme suggéré par @patelb answer.
membresound
Ca ne fonctionne pas. Mais la réponse de patelib fonctionne juste hors de la boîte!
Mykhaylo Kopytonenko
En guise d'avertissement, le nôtre avait besoin de l' @JsonSerializeannotation mentionnée par Nick pour le faire fonctionner.
pennstatephil
92

J'ai ajouté la com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1dépendance et j'ai commencé à obtenir la date au format suivant:

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

ce n'est pas ce que je voulais mais je me rapprochais. J'ai ensuite ajouté ce qui suit

spring.jackson.serialization.write_dates_as_timestamps=false

au fichier application.properties qui m'a donné le format correct dont j'avais besoin.

"birthDate": "2016-01-25T21:34:55"
Patelb
la source
2
Travaillé hors de la boîte lors de l'inclusion de la jackson-datatype-jsr310dépendance. Cela devrait être la réponse acceptée.
membresound
6
En tant que FYI, la partie application.properties peut être effectuée via la configuration Java si vous configurez l'ObjectMapper avec:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin
31

Le voici dans maven, avec la propriété pour que vous puissiez survivre entre les mises à niveau de démarrage de printemps

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Matt Broekhuis
la source
1
Utilisez cette solution avec le commentaire @NickGrealy: Cela vaut probablement la peine d'inclure l'annotation suivante -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog
19

1) Dépendance

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

2) Annotation au format date-heure.

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;
    }
}
Yan Khonski
la source
2
Merci beaucoup. @JsonFormat est celui qui l'a résolu pour moi. Aucune des solutions ci - dessus a fonctionné pour moi pour une raison quelconque
theprogrammer
7

J'ai trouvé une autre solution que vous pouvez convertir dans le format de votre choix et appliquer à tous les types de données LocalDateTime et vous n'avez pas à spécifier @JsonFormat au-dessus de chaque type de données LocalDateTime. ajoutez d'abord la dépendance:

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

Ajoutez le haricot suivant:

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

dans votre fichier de configuration, ajoutez ce qui suit:

@Import(Java8DateTimeConfiguration.class)

Cela sérialisera et dé-sérialisera toutes les propriétés LocalDateTime et ZonedDateTime tant que vous utilisez objectMapper créé par spring.

Le format que vous avez obtenu pour ZonedDateTime est: "2017-12-27T08: 55: 17.317 + 02: 00 [Asia / Jerusalem]" pour LocalDateTime est: "2017-12-27T09: 05: 30.523"

Ida Amit
la source
Vous devez probablement remplacer val bean par SimpleModule bean = new SimpleModule ();
Abhishek Galoda
Est-ce pour Java 9?
Daniel Dai
@DanielDai C'est en Java 8.
Ida Amit
@Abhi, SimpleModules'étend Module. Moduleest un résumé. val bean = new SimpleModule(); fonctionne parfaitement.
Ida Amit
pour SpringBoot version 1.5.3.RELEASE n'était pas nécessaire d'ajouter la dépendance jackson-datatype-jsr310.
Moon13
7

Ecrire cette réponse comme un rappel pour moi aussi.

J'ai combiné plusieurs réponses ici et à la fin la mienne a fonctionné avec quelque chose comme ça. (J'utilise SpringBoot 1.5.7 et 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
la source
1
vous voudrez peut-être ajouter des importations pour DateTimeFormatet d'autres.
prière
4

Cela fonctionne bien:

Ajoutez la dépendance:

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

Ajoutez l'annotation:

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

Maintenant, vous devez obtenir le format correct.

Pour utiliser le mappeur d'objets, vous devez enregistrer le JavaTime

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

Comme déjà mentionné, spring-boot récupérera tout ce dont vous avez besoin (pour le démarrage web et webflux).

Mais ce qui est encore mieux: vous n'avez pas besoin d'enregistrer vous-même des modules. Jetez un œil ici . Depuis @SpringBootApplicationutilise @EnableAutoConfigurationsous le capot, cela signifie JacksonAutoConfigurationsera ajouté au contexte automatiquement. Maintenant, si vous regardez à l'intérieur JacksonAutoConfiguration, vous verrez:

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

Ce type sera appelé dans le processus d'initialisation et récupérera tous les modules qu'il peut trouver dans le classpath. (J'utilise Spring Boot 2.1)

yuranos
la source
1

J'utilise Springboot 2.0.6 et pour une raison quelconque, les modifications yml de l'application n'ont pas fonctionné. Et aussi j'avais plus d'exigences.

J'ai essayé de créer ObjectMapper et de le marquer comme primaire, mais Spring Boot s'est plaint que j'avais déjà jacksonObjectMapper comme marqué Primary !!

Voilà donc ce que j'ai fait. J'ai apporté des modifications au mappeur interne.

Mon sérialiseur et mon désérialiseur sont spéciaux - ils traitent de «jj / MM / AAAA»; et lors de la désérialisation - il fait de son mieux pour utiliser le format populaire 3-4 pour s'assurer que j'ai du 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
la source
0

@JsonDeserialize(using= LocalDateDeserializer.class) ne fonctionne pas pour moi avec la dépendance ci-dessous.

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

J'ai utilisé le convertisseur de code ci-dessous pour désérialiser la date en un fichier 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
la source
0

utilisez simplement:

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

ou vous pouvez utiliser le modèle comme vous le souhaitez, par exemple: ('-' in place of '/')

Ashish Dwivedi
la source
Cette réponse a déjà été partagée, mais avec la syntaxe correcte.
Erik Pragt
0

J'utilise Spring Boot 2.1.8. J'ai importé

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

qui inclut le jackson-datatype-jsr310 .

Ensuite, j'ai dû ajouter ces annotations

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

et il fonctionne. Le JSON ressemble à ceci:

"date": "2020-03-09 17:55:00"
Tomas Lukac
la source
Astuce: inclure spring-boot-starter-jsonn'est nécessaire que s'il spring-boot-starter-webmanque.
judomu