JSON Java 8 LocalDateTime-Format im Spring Boot

111

Ich habe ein kleines Problem beim Formatieren von Java 8 LocalDateTime in meiner Spring Boot-Anwendung. Mit 'normalen' Daten habe ich kein Problem, aber die LocalDateTime-Felder werden in Folgendes konvertiert:

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

Während ich es in etwas konvertieren möchte wie:

"startDate": "2015-01-01"

Mein Code sieht folgendermaßen aus:

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

Aber eine der oben genannten Anmerkungen funktioniert nicht, das Datum wird immer wieder wie oben formatiert. Vorschläge willkommen!

Erik Pragt
quelle

Antworten:

133

Update : Spring Boot 2.x benötigt diese Konfiguration nicht mehr. Ich habe hier eine aktuellere Antwort geschrieben .


(Dies ist die Methode vor Spring Boot 2.x. Dies kann für Benutzer nützlich sein, die an einer älteren Version von Spring Boot arbeiten.)

Ich habe hier endlich gefunden , wie es geht. Um das Problem zu beheben, benötigte ich eine weitere Abhängigkeit:

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

Durch Einbeziehen dieser Abhängigkeit registriert Spring automatisch einen Konverter dafür, wie hier beschrieben . Danach müssen Sie application.properties Folgendes hinzufügen:

spring.jackson.serialization.write_dates_as_timestamps=false

Dadurch wird sichergestellt, dass ein korrekter Konverter verwendet wird und die Daten im Format gedruckt werden 2016-03-16T13:56:39.492

Anmerkungen werden nur benötigt, wenn Sie das Datumsformat ändern möchten.

Erik Pragt
quelle
24
Wahrscheinlich lohnt es sich, die folgende Anmerkung aufzunehmen - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy
3
Wahrscheinlich ist es besser, nur einen application.propertiesEintrag zu verwenden, wie in der Antwort von @patelb vorgeschlagen.
Membersound
Funktioniert nicht. Aber Patelibs Antwort funktioniert sofort!
Mykhaylo Kopytonenko
Als Heads-Up brauchten wir die @JsonSerializeAnmerkung, die Nick erwähnte, um sie zum Laufen zu bringen.
Pennstatephil
91

Ich fügte die com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1Abhängigkeit hinzu und begann, das Datum im folgenden Format abzurufen:

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

Das ist nicht das, was ich wollte, aber ich kam näher. Ich habe dann folgendes hinzugefügt

spring.jackson.serialization.write_dates_as_timestamps=false

in die Datei application.properties, die mir das richtige Format gab, das ich brauchte.

"birthDate": "2016-01-25T21:34:55"
patelb
quelle
2
Beim Einbeziehen der jackson-datatype-jsr310Abhängigkeit sofort funktioniert . Dies sollte die akzeptierte Antwort sein.
Membersound
5
Als FYI kann der application.properties-Teil über die Java-Konfiguration ausgeführt werden, wenn Sie den mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
ObjectMapper
31

Hier ist es in Maven, mit der Eigenschaft, so dass Sie zwischen Spring Boot Upgrades überleben können

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Matt Broekhuis
quelle
1
Verwenden Sie diese Lösung mit @ NickGrealy Kommentar: Wahrscheinlich lohnt es sich, die folgende Anmerkung aufzunehmen -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog
19

1) Abhängigkeit

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

2) Anmerkung im Datums- / Uhrzeitformat.

public class RestObject {

    private LocalDateTime timestamp;

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

3) Federkonfiguration

@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
quelle
2
Vielen Dank. @JsonFormat ist derjenige, der es für mich gelöst hat. Keine der oben genannten Lösungen hat aus irgendeinem Grund für mich
funktioniert
7

Ich habe eine andere Lösung gefunden, mit der Sie sie in ein beliebiges Format konvertieren und auf alle LocalDateTime-Datentypen anwenden können. Sie müssen @JsonFormat nicht über jedem LocalDateTime-Datentyp angeben. Fügen Sie zuerst die Abhängigkeit hinzu:

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

Fügen Sie die folgende Bohne hinzu:

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

Fügen Sie in Ihrer Konfigurationsdatei Folgendes hinzu:

@Import(Java8DateTimeConfiguration.class)

Dadurch werden alle Eigenschaften LocalDateTime und ZonedDateTime serialisiert und de-serialisiert, solange Sie den von spring erstellten objectMapper verwenden.

Das Format, das Sie für ZonedDateTime erhalten haben, lautet: "2017-12-27T08: 55: 17.317 + 02: 00 [Asia / Jerusalem]" für LocalDateTime lautet: "2017-12-27T09: 05: 30.523"

Ida Amit
quelle
Wahrscheinlich müssen Sie val bean durch SimpleModule bean = new SimpleModule () ersetzen.
Abhishek Galoda
Ist das für Java 9?
Daniel Dai
@ DanielDai Dies ist in Java 8.
Ida Amit
@Abhi, SimpleModuleerweitert Module. Moduleist eine Zusammenfassung. val bean = new SimpleModule(); funktioniert perfekt.
Ida Amit
für SpringBoot Version 1.5.3.RELEASE war nicht erforderlich, um die Abhängigkeit jackson-datatype-jsr310 hinzuzufügen.
Moon13
7

Schreiben Sie diese Antwort auch als Erinnerung für mich.

Ich habe hier mehrere Antworten kombiniert und am Ende haben meine mit so etwas gearbeitet. (Ich verwende SpringBoot 1.5.7 und 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
quelle
1
Möglicherweise möchten Sie Importe für DateTimeFormatund andere hinzufügen .
Prayagupd
4

Das funktioniert gut:

Fügen Sie die Abhängigkeit hinzu:

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

Fügen Sie die Anmerkung hinzu:

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

Jetzt müssen Sie das richtige Format erhalten.

Um Object Mapper verwenden zu können, müssen Sie JavaTime registrieren

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

Wie bereits erwähnt, ruft Spring-Boot alles ab, was Sie benötigen (sowohl für den Web- als auch für den Webflux-Starter).

Aber was ist noch besser - Sie müssen keine Module selbst registrieren. Schauen Sie hier . Da die @SpringBootApplicationVerwendung @EnableAutoConfigurationunter der Haube erfolgt, werden sie JacksonAutoConfigurationautomatisch zum Kontext hinzugefügt. Wenn Sie jetzt hineinschauen JacksonAutoConfiguration, werden Sie sehen:

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

Dieser Typ wird während der Initialisierung aufgerufen und ruft alle Module ab, die er im Klassenpfad finden kann. (Ich benutze Spring Boot 2.1)

Yuranos
quelle
1

Ich verwende Springboot 2.0.6 und aus irgendeinem Grund haben die Änderungen an der App yml nicht funktioniert. Und ich hatte auch mehr Anforderungen.

Ich habe versucht, ObjectMapper zu erstellen und als primär zu markieren, aber Spring Boot hat sich beschwert, dass ich jackOObjectMapper bereits als primär markiert habe !!

Das habe ich also getan. Ich habe Änderungen am internen Mapper vorgenommen.

Mein Serializer und Deserializer sind etwas Besonderes - sie befassen sich mit 'TT / MM / JJJJ'; und während der De-Serialisierung versucht es sein Bestes, 3-4 beliebte Formate zu verwenden, um sicherzustellen, dass ich ein lokales Datum habe.

@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
quelle
0

@JsonDeserialize(using= LocalDateDeserializer.class) funktioniert bei mir mit der folgenden abhängigkeit nicht.

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

Ich habe den folgenden Code-Konverter verwendet, um das Datum in ein zu deserialisieren 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
quelle
0

Verwenden Sie einfach:

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

oder Sie können Muster verwenden, wie Sie möchten, z. ('-' in place of '/')

Ashish Dwivedi
quelle
Diese Antwort wurde bereits zuvor geteilt, jedoch mit der richtigen Syntax.
Erik Pragt
0

Ich benutze Spring Boot 2.1.8. Ich habe importiert

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

welches den jackson-datatype-jsr310 enthält .

Dann musste ich diese Anmerkungen hinzufügen

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

und es funktioniert. Der JSON sieht folgendermaßen aus:

"date": "2020-03-09 17:55:00"
Tomas Lukac
quelle
Hinweis: Einschließen spring-boot-starter-jsonist nur erforderlich, wenn spring-boot-starter-webes fehlt.
Judomu