Formato JSON Java 8 LocalDateTime no Spring Boot

112

Estou tendo um pequeno problema com a formatação de um LocalDateTime Java 8 em meu aplicativo Spring Boot. Com datas 'normais', não tenho problemas, mas os campos LocalDateTime são convertidos para o seguinte:

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

Embora eu queira convertê-lo para algo como:

"startDate": "2015-01-01"

Meu código é parecido com este:

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

Mas qualquer uma das anotações acima não funciona, a data continua sendo formatada como acima. Sugestões bem-vindas!

Erik Pragt
fonte

Respostas:

134

atualização : Spring Boot 2.x não requer mais esta configuração. Eu escrevi uma resposta mais atualizada aqui .


(Esta é a maneira de fazer antes do Spring Boot 2.x, pode ser útil para pessoas que trabalham em uma versão mais antiga do Spring Boot)

Finalmente descobri aqui como fazer. Para consertar, eu precisava de outra dependência:

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

Incluindo essa dependência, o Spring registrará automaticamente um conversor para ela, conforme descrito aqui . Depois disso, você precisa adicionar o seguinte a application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

Isso irá garantir que um conversor correto seja usado e as datas sejam impressas no formato de 2016-03-16T13:56:39.492

As anotações são necessárias apenas no caso de você desejar alterar o formato da data.

Erik Pragt
fonte
24
Provavelmente, vale a pena incluir a seguinte anotação - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy
3
Provavelmente melhor usar apenas uma application.propertiesentrada, como sugerido pela resposta @patelb.
membros em
Não está funcionando. Mas a resposta do patelib funciona fora da caixa!
Mykhaylo Kopytonenko
Como um alerta, o nosso precisava da @JsonSerializeanotação que Nick mencionou para fazê-lo funcionar.
pennstatephil
92

Adicionei a com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1dependência e comecei a obter a data no seguinte formato:

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

o que não era o que eu queria, mas estava chegando perto. Eu então adicionei o seguinte

spring.jackson.serialization.write_dates_as_timestamps=false

para o arquivo application.properties que me forneceu o formato correto que eu precisava.

"birthDate": "2016-01-25T21:34:55"
patelb
fonte
2
Funcionou fora da caixa ao incluir a jackson-datatype-jsr310dependência. Esta deve ser a resposta aceita.
membros em
6
Como um FYI, a parte application.properties pode ser feita por meio da configuração Java se você estiver configurando o ObjectMapper com:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin
31

Aqui está em maven, com a propriedade para que você possa sobreviver entre atualizações de botas de primavera

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Matt Broekhuis
fonte
1
Use esta solução com o comentário @NickGrealy: Provavelmente vale a pena incluir a seguinte anotação -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog
19

1) Dependência

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

2) Anotação com formato data-hora.

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
fonte
2
Muito obrigado. @JsonFormat é quem resolveu isso para mim. Nenhuma das soluções acima funcionou para mim por algum motivo
programador de
7

Eu encontrei outra solução que você pode converter para qualquer formato que você quiser e aplicar a todos os tipos de dados LocalDateTime e você não precisa especificar @JsonFormat acima de todos os tipos de dados LocalDateTime. primeiro adicione a dependência:

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

Adicione o seguinte 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;
    }
}

em seu arquivo de configuração, adicione o seguinte:

@Import(Java8DateTimeConfiguration.class)

Isso serializará e desserializará todas as propriedades LocalDateTime e ZonedDateTime, desde que você esteja usando o objectMapper criado na primavera.

O formato que você obteve para ZonedDateTime é: "2017-12-27T08: 55: 17.317 + 02: 00 [Ásia / Jerusalém]" para LocalDateTime é: "2017-12-27T09: 05: 30.523"

Ida Amit
fonte
Provavelmente, você precisa substituir val bean por SimpleModule bean = new SimpleModule ();
Abhishek Galoda
É para Java 9?
Daniel Dai
@DanielDai Isto está em Java 8.
Ida Amit
@Abhi, SimpleModuleestende Module. Moduleé um abstrato. val bean = new SimpleModule(); funciona perfeito.
Ida Amit
para SpringBoot versão 1.5.3.RELEASE não foi necessário adicionar a dependência jackson-datatype-jsr310.
Lua13,
7

Escrevendo esta resposta como um lembrete para mim também.

Combinei várias respostas aqui e no final a minha funcionou com algo assim. (Estou 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;

}
JWiryo
fonte
1
você pode querer adicionar importações para DateTimeFormate outros.
prayagupd
4

Isso funciona bem:

Adicione a dependência:

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

Adicione a anotação:

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

Agora, você deve obter o formato correto.

Para usar o mapeador de objetos, você precisa registrar o JavaTime

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

Como já mencionamos, o spring-boot irá buscar tudo que você precisa (tanto para web quanto para iniciante do webflux).

Mas o que é ainda melhor - você não precisa registrar nenhum módulo. Dê uma olhada aqui . Uma vez que @SpringBootApplicationusa @EnableAutoConfigurationsob o capô, significa JacksonAutoConfigurationque será adicionado ao contexto automaticamente. Agora, se você olhar para dentro JacksonAutoConfiguration, verá:

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

Este fella será chamado no processo de inicialização e irá buscar todos os módulos que puder encontrar no classpath. (Eu uso Spring Boot 2.1)

yuranos
fonte
1

Estou usando o Springboot 2.0.6 e, por algum motivo, as alterações do yml do aplicativo não funcionaram. E também tive mais requisitos.

Tentei criar ObjectMapper e marcá-lo como Primário, mas o Spring Boot reclamou que já tenho jacksonObjectMapper marcado como Primário !!

Então foi isso que eu fiz. Fiz alterações no mapeador interno.

Meu serializador e desserializador são especiais - eles lidam com 'dd / MM / AAAA'; e durante a desserialização - ele tenta o seu melhor para usar o formato popular 3-4 para se certificar de que tenho algum 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
fonte
0

@JsonDeserialize(using= LocalDateDeserializer.class) não funciona para mim com a dependência abaixo.

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

Usei o conversor de código abaixo para desserializar a data em um 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
fonte
0

simplesmente use:

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

ou você pode usar o padrão como quiser, por exemplo: ('-' in place of '/')

Ashish Dwivedi
fonte
Esta resposta já foi compartilhada antes, mas com a sintaxe correta.
Erik Pragt
0

Estou usando o Spring Boot 2.1.8. Eu importei

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

que inclui o jackson-datatype-jsr310 .

Então, eu tive que adicionar essas anotações

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

e funciona. O JSON se parece com isto:

"date": "2020-03-09 17:55:00"
Tomas Lukac
fonte
Dica: a inclusão spring-boot-starter-jsonsó é necessária se spring-boot-starter-webestiver faltando.
Judomu