Формат JSON Java 8 LocalDateTime в Spring Boot

У меня возникла небольшая проблема с форматированием Java 8 LocalDateTime в моем приложении Spring Boot. С "нормальными" датами у меня проблем нет, но поля LocalDateTime преобразуются в следующие:

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

Хотя я бы хотел преобразовать его во что-то вроде:

"startDate": "2015-01-01"

Мой код выглядит так:

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

Но ни одна из вышеперечисленных аннотаций не работает, дата продолжает форматироваться, как указано выше. Предложения приветствуются!


person Erik Pragt    schedule 29.04.2015    source источник


Ответы (15)


Я наконец нашел здесь, как сделать это. Чтобы исправить это, мне понадобилась еще одна зависимость:

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

Включив эту зависимость, Spring автоматически зарегистрирует для нее конвертер, как описано здесь. После этого вам нужно добавить в application.properties следующее:

spring.jackson.serialization.write_dates_as_timestamps=false

Это обеспечит использование правильного конвертера, а даты будут напечатаны в формате 2016-03-16T13:56:39.492.

Аннотации нужны только в том случае, если вы хотите изменить формат даты.

person Erik Pragt    schedule 30.04.2015
comment
Наверное, стоит добавить следующую аннотацию - _1 _... - person Nick Grealy; 28.06.2016
comment
Вероятно, лучше просто использовать запись application.properties, как предлагает ответ @patelb. - person membersound; 06.06.2017
comment
Не работает. Но ответ PATELIB работает из коробки! - person Mykhaylo Kopytonenko; 01.11.2017
comment
Напомним, что нам нужна была аннотация @JsonSerialize, о которой упоминал Ник, чтобы заставить ее работать. - person pennstatephil; 06.03.2018
comment
В моем случае это сработало: @JsonSerialize(using = LocalDateSerializer.class) и @JsonFormat(pattern="yyyy-MM-dd"), никаких новых зависимостей и свойств. - person user3791111; 03.10.2020
comment
Ссылка в части обновления не работает - person findusl; 15.12.2020

Я добавил зависимость com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1 и начал получать дату в следующем формате:

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

это не то, что я хотел, но я приближался. Затем я добавил следующее

spring.jackson.serialization.write_dates_as_timestamps=false

в файл application.properties, который дал мне нужный формат.

"birthDate": "2016-01-25T21:34:55"
person patelb    schedule 27.01.2016
comment
Сработало из коробки при включении зависимости jackson-datatype-jsr310. Это должен быть принятый ответ. - person membersound; 06.06.2017
comment
К вашему сведению, часть application.properties может быть выполнена через конфигурацию Java, если вы настраиваете ObjectMapper с помощью: mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - person justin; 17.06.2017

Вот он в maven, со свойством, чтобы вы могли выжить между весенними обновлениями загрузки.

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
person Matt Broekhuis    schedule 13.05.2016
comment
Используйте это решение с комментарием @NickGrealy: вероятно, стоит включить следующую аннотацию - @JsonSerialize(using = LocalDateTimeSerializer.class) - person HairOfTheDog; 29.03.2018

1) Зависимость

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

2) Аннотация с форматом даты и времени.

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;
    }
}
person Yan Khonski    schedule 05.06.2017
comment
Огромное спасибо. @JsonFormat - это тот, кто решил это за меня. Ни одно из вышеперечисленных решений по какой-то причине не помогло мне - person theprogrammer; 11.02.2020
comment
Есть ли аналогичная конфигурация для ObjectWriter, поскольку мне нужно преобразовать объект как строку и хранилище в БД, но поле LocalDateTime выходит через запятую. - person Sameer; 04.04.2021

Я пишу этот ответ как напоминание.

Я объединил здесь несколько ответов, и в итоге мой работал с чем-то вроде этого. (Я использую SpringBoot 1.5.7 и 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;

}
person JWiryo    schedule 10.10.2018
comment
вы можете добавить импорт для DateTimeFormat и других. - person prayagupd; 12.05.2019
comment
Откуда появился LocalDateTimeDeserializer? - person Martin P.; 16.03.2021
comment
Вы можете проверить принятый ответ Эрика Прагта, он упомянул, что добавил зависимость Джексона, вот откуда она. Надеюсь, это ответит на ваш вопрос - person JWiryo; 17.03.2021

Я нашел другое решение, которое вы можете преобразовать в любой формат, который хотите, и применить ко всем типам данных LocalDateTime, и вам не нужно указывать @JsonFormat над каждым типом данных LocalDateTime. сначала добавьте зависимость:

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

Добавьте следующий bean-компонент:

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

в вашем файле конфигурации добавьте следующее:

@Import(Java8DateTimeConfiguration.class)

Это приведет к сериализации и десериализации всех свойств LocalDateTime и ZonedDateTime, если вы используете objectMapper, созданный spring.

Формат ZonedDateTime: «2017-12-27T08: 55: 17.317 + 02: 00 [Азия / Иерусалим]» для LocalDateTime: «2017-12-27T09: 05: 30.523».

person Ida Amit    schedule 27.12.2017
comment
Вероятно, вам нужно заменить bean-компонент val на bean-компонент SimpleModule = new SimpleModule (); - person Abhishek Galoda; 18.01.2018
comment
Это для Java 9? - person Daniel Dai; 15.03.2018
comment
@DanielDai Это в Java 8. - person Ida Amit; 17.03.2018
comment
@Abhi, SimpleModule расширяет Module. Module - это абстракция. val bean = new SimpleModule(); отлично работает. - person Ida Amit; 17.03.2018
comment
для SpringBoot версии 1.5.3.RELEASE не требовалось добавлять зависимость jackson-datatype-jsr310. - person Moon13; 24.04.2018

Это нормально работает:

Добавьте зависимость:

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

Добавьте аннотацию:

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

Теперь вы должны получить правильный формат.

Чтобы использовать объектный сопоставитель, вам необходимо зарегистрировать JavaTime

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
person Manuel Antonio    schedule 05.07.2018

Я использую Spring Boot 2.1.8. Я импортировал

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

который включает jackson-datatype-jsr310.

Затем мне пришлось добавить эти аннотации

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

и это работает. JSON выглядит так:

"date": "2020-03-09 17:55:00"
person Tomas Lukac    schedule 11.03.2020
comment
Подсказка: включение spring-boot-starter-json необходимо только в том случае, если spring-boot-starter-web отсутствует. - person judomu; 06.05.2020

Как уже упоминалось, spring-boot загрузит все, что вам нужно (как для веба, так и для стартера webflux).

Но что еще лучше - вам не нужно самостоятельно регистрировать какие-либо модули. Взгляните на здесь. Поскольку @SpringBootApplication использует @EnableAutoConfiguration под капотом, это означает, что JacksonAutoConfiguration будет добавлен в контекст автоматически. Теперь, если вы заглянете внутрь JacksonAutoConfiguration, вы увидите:

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

Этот парень будет вызван в процессе инициализации и получит все модули, которые он может найти в пути к классам. (Я использую Spring Boot 2.1)

person yuranos    schedule 19.12.2018

Это сработало для меня.

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
public Class someClass {

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    private LocalDateTime sinceDate;

}

person Subash    schedule 07.12.2020

Я использую Springboot 2.0.6, и по какой-то причине изменения yml приложения не работали. А еще у меня было больше требований.

Я попытался создать ObjectMapper и пометить его как Primary, но весенняя загрузка пожаловалась, что у меня уже есть jacksonObjectMapper с пометкой Primary !!

Вот что я сделал. Я внес изменения во внутренний картограф.

Мои сериализатор и десериализатор особенные - они имеют дело с 'дд / ММ / ГГГГ'; и при десериализации - он изо всех сил пытается использовать 3-4 популярных формата, чтобы убедиться, что у меня есть 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;
}
person Kalpesh Soni    schedule 17.04.2019

@JsonDeserialize(using= LocalDateDeserializer.class) не работает для меня с приведенной ниже зависимостью.

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

Я использовал приведенный ниже конвертер кода для десериализации даты в 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();
    }
}
person Ram    schedule 27.09.2018

просто используйте:

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

или вы можете использовать шаблон по своему усмотрению, например: ('-' in place of '/')

person Ashish Dwivedi    schedule 29.04.2019
comment
Этим ответом публиковались раньше, но с правильным синтаксисом. - person Erik Pragt; 30.04.2019

Добавлен

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

в блок компиляции gradle

и в application.yml файле

spring:
  jackson:
    serialization:
      write_dates_as_timestamps: false

если вы используете application.properties файл, добавьте следующее

spring.jackson.serialization.write_dates_as_timestamps=false

если вы хотите применить собственный формат, вы можете применить аннотацию

    @JsonFormat(pattern = "yyyy-MMMM-dd hh:mm:ss")
    private LocalDateTime date;

У меня стало нормально работать

person manoj    schedule 27.04.2021

Это сработало для меня.

Я определил поле BirthDate в моем DTO, как указано ниже:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birthDate;

И в моем теле запроса я передал BirthDate в следующем формате:

{
   "birthDate": "2021-06-03 00:00:00"
 }
person Ruchir Dixit    schedule 06.06.2021