Format JSON Java 8 LocalDateTime w Spring Boot

112

Mam mały problem z formatowaniem Java 8 LocalDateTime w mojej aplikacji Spring Boot. Z „normalnymi” datami nie mam problemu, ale pola LocalDateTime są konwertowane na następujące:

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

Chociaż chciałbym przekonwertować to na coś takiego:

"startDate": "2015-01-01"

Mój kod wygląda tak:

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

Ale żadna z powyższych adnotacji nie działa, data jest formatowana jak powyżej. Sugestie mile widziane!

Erik Pragt
źródło

Odpowiedzi:

135

aktualizacja : Spring Boot 2.x nie wymaga już tej konfiguracji. Napisałem tutaj bardziej aktualną odpowiedź .


(Jest to sposób na zrobienie tego przed Spring Boot 2.x, może być przydatny dla osób pracujących na starszej wersji Spring Boot)

W końcu znalazłem tutaj, jak to zrobić. Aby to naprawić, potrzebowałem innej zależności:

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

Uwzględniając tę ​​zależność, Spring automatycznie zarejestruje dla niej konwerter, jak opisano tutaj . Następnie musisz dodać następujące elementy do application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Zapewni to użycie prawidłowego konwertera, a daty będą drukowane w formacie 2016-03-16T13:56:39.492

Adnotacje są potrzebne tylko w przypadku, gdy chcesz zmienić format daty.

Erik Pragt
źródło
25
Chyba warto dodać następującą adnotację - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy
3
Prawdopodobnie lepiej po prostu użyć application.propertieswpisu, jak sugeruje @patelb odpowiedź.
członkowie wokół
Nie działa. Ale odpowiedź Patelib po prostu działa po wyjęciu z pudełka!
Mykhaylo Kopytonenko
Aby to zadziałało, potrzebowaliśmy @JsonSerializeadnotacji, o której wspomniał Nick.
pennstatephil
92

Dodałem com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1zależność i zacząłem pobierać datę w następującym formacie:

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

co nie jest tym, czego chciałem, ale zbliżałem się. Następnie dodałem następujące

spring.jackson.serialization.write_dates_as_timestamps=false

do pliku application.properties, który dał mi właściwy format, którego potrzebowałem.

"birthDate": "2016-01-25T21:34:55"
patelb
źródło
2
Wypracowano po wyjęciu z pudełka, gdy uwzględniono jackson-datatype-jsr310zależność. To powinna być akceptowana odpowiedź.
członkowie około
6
Jako informacja do Twojej wiadomości, część application.properties można wykonać za pomocą konfiguracji Java, jeśli konfigurujesz ObjectMapper za pomocą:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin
31

Tutaj jest w maven, z posiadłością, dzięki której możesz przetrwać między wiosennymi aktualizacjami butów

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Matt Broekhuis
źródło
1
Użyj tego rozwiązania z komentarzem @NickGrealy: Prawdopodobnie warto dołączyć następującą adnotację -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog
19

1) Zależność

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

2) Adnotacja z formatem daty i godziny.

public class RestObject {

    private LocalDateTime timestamp;

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

3) Konfiguracja sprężyn.

@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
źródło
2
Dzięki wielkie. @JsonFormat jest tym, który rozwiązał to dla mnie. Żadne z powyższych rozwiązań nie pracował dla mnie z jakiegoś powodu
theprogrammer
7

Znalazłem inne rozwiązanie, które możesz przekonwertować na dowolny format i zastosować do wszystkich typów danych LocalDateTime i nie musisz określać @JsonFormat nad każdym typem danych LocalDateTime. najpierw dodaj zależność:

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

Dodaj następującą fasolę:

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

w pliku konfiguracyjnym dodaj:

@Import(Java8DateTimeConfiguration.class)

Spowoduje to serializację i deserializację wszystkich właściwości LocalDateTime i ZonedDateTime, o ile używasz objectMapper utworzonego wiosną.

Format uzyskany dla ZonedDateTime to: „2017-12-27T08: 55: 17.317 + 02: 00 [Azja / Jerozolima]” dla LocalDateTime to: „2017-12-27T09: 05: 30.523”

Ida Amit
źródło
Prawdopodobnie musisz zamienić fasolę val na fasolę SimpleModule = nowy moduł SimpleModule ();
Abhishek Galoda
Czy to dla Java 9?
Daniel Dai
@DanielDai To jest w Javie 8.
Ida Amit
@Abhi, SimpleModulerozszerza Module. Modulejest abstrakcją. val bean = new SimpleModule(); działa idealnie.
Ida Amit
dla SpringBoot w wersji 1.5.3. RELEASE nie było konieczne, aby dodać zależność jackson-datatype-jsr310.
Moon 13
7

Pisząc tę ​​odpowiedź również jako przypomnienie dla mnie.

Połączyłem tutaj kilka odpowiedzi i ostatecznie moja działała z czymś takim. (Używam SpringBoot 1.5.7 i 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
źródło
1
możesz chcieć dodać importy dla DateTimeFormati innych.
prayagupd
4

To działa dobrze:

Dodaj zależność:

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

Dodaj adnotację:

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

Teraz musisz uzyskać właściwy format.

Aby użyć mapowania obiektów, musisz zarejestrować JavaTime

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Manuel Antonio
źródło
1

Jak już wspomniano, spring-boot pobierze wszystko, czego potrzebujesz (zarówno dla startera internetowego, jak i webflux).

Ale co lepsze - nie musisz samodzielnie rejestrować żadnych modułów. Spójrz tutaj . Ponieważ @SpringBootApplicationużywa @EnableAutoConfigurationpod maską, oznacza to, że JacksonAutoConfigurationzostanie automatycznie dodany do kontekstu. Teraz, jeśli zajrzysz do środka JacksonAutoConfiguration, zobaczysz:

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

Ten facet zostanie wywołany w trakcie inicjalizacji i pobierze wszystkie moduły, które może znaleźć w ścieżce klas. (Używam Spring Boot 2.1)

yuranos
źródło
1

Używam Springboot 2.0.6 iz jakiegoś powodu zmiany w aplikacji yml nie zadziałały. Miałem też więcej wymagań.

Próbowałem stworzyć ObjectMapper i oznaczyć go jako Primary, ale spring boot narzekał, że mam już jacksonObjectMapper jako oznaczony jako Primary !!

Więc to właśnie zrobiłem. Dokonałem zmian w wewnętrznym programie mapującym.

Mój Serializer i Deserializer są wyjątkowe - zajmują się 'dd / MM / YYYY'; a podczas de-serializacji - stara się jak najlepiej wykorzystać 3-4 popularne formaty, aby upewnić się, że mam jakieś 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
źródło
0

@JsonDeserialize(using= LocalDateDeserializer.class) nie działa dla mnie z poniższą zależnością.

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

Użyłem poniższego konwertera kodu do deserializacji daty do pliku 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();
    }
}
Baran
źródło
0

po prostu użyj:

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

lub możesz użyć wzoru, jak chcesz, na przykład: ('-' in place of '/')

Ashish Dwivedi
źródło
Ta odpowiedź została wcześniej udostępniona, ale z poprawną składnią.
Erik Pragt
0

Używam Spring Boot 2.1.8. Sprowadziłem

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

który zawiera plik jackson-datatype-jsr310 .

Następnie musiałem dodać te adnotacje

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

i to działa. JSON wygląda następująco:

"date": "2020-03-09 17:55:00"
Tomas Lukac
źródło
Wskazówka: dołączanie spring-boot-starter-jsonjest potrzebne tylko wtedy, gdy go spring-boot-starter-webbrakuje.
judomu