10

I'm trying to deserialize a String to LocalDateTime with Jackson but it doesn't work.

I have a data class with a LocalDateTime field:

@Data
public class Registration {
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
    private LocalDateTime timestamp;
}

I added the special Jackson datatype modules:

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

The serialization works fine with:

new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(registration);

Result String:

{"timestamp":"2018-09-03 10:09:35"}

But the deserialization doesn't work with:

new ObjectMapper().registerModule(new JavaTimeModule()).readValue(json.traverse(), Registration.class)

As error I get:

Cannot deserialize value of type `java.time.LocalDateTime` from String "2018-09-03 10:09:35": 
    Failed to deserialize java.time.LocalDateTime: 
        (java.time.format.DateTimeParseException) Text '2018-09-03 10:09:35' could not be parsed: 
            Unable to obtain LocalDateTime from TemporalAccessor: 
                {MinuteOfHour=9, NanoOfSecond=0, SecondOfMinute=35, MicroOfSecond=0, MilliOfSecond=0, HourOfAmPm=10},
                ISO resolved to 2018-09-03 of type java.time.format.Parsed

What am I missing? I'm confused that the serialization works but the deserialization doesn't.


MWE: (small gradle Java project)

Main.java:

import java.io.IOException;
import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class Main {

    public static void main(String[] args) throws IOException {
        Registration registration = new Registration();
        registration.setTimestamp(LocalDateTime.now());

         ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());

         String s = objectMapper.writeValueAsString(registration);
         TreeNode treeNode = objectMapper.readTree(s);

         //Fails here:
         Registration registration1 = objectMapper.readValue(treeNode.traverse(), Registration.class);

        System.out.println(registration1);
    }
}

class Registration {
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
    private LocalDateTime timestamp;

    public Registration() {
    }

    public LocalDateTime getTimestamp() {
        return this.timestamp;
    }

    public void setTimestamp(LocalDateTime localDateTime) {
        this.timestamp = localDateTime;
    }
}

build.gradle:

plugins {
    id 'java'
}

group 'dateMWE'
version '1.0-SNAPSHOT'

sourceCompatibility = 10

repositories {
    mavenCentral()
}

dependencies {
    compile("com.fasterxml.jackson.core:jackson-annotations:2.9.6")
    compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.6")
}
Spenhouet
  • 6,556
  • 12
  • 51
  • 76

2 Answers2

19

The problem is not related with JSON deserialization, but rather with the time format string:

pattern = "yyyy-MM-dd hh:mm:ss"

Please notice that the hours are set as hh: this is a 12-hour formatter, which requires "AM" or "PM" values.

If the pattern is changed to

pattern = "yyyy-MM-dd HH:mm:ss"

the problem should be solved.

Antot
  • 3,904
  • 1
  • 21
  • 27
  • That is true. I played around with the pattern but I couldn't find any detailed documentation on what patterns/keywords exist. Could you add a link to a documentation or listing of all possible format options? – Spenhouet Sep 03 '18 at 09:44
  • 1
    @Spen: DataTimeFormatter patterns are documented in the standard [Java API documentation](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) – Antot Sep 03 '18 at 09:46
  • Thansk for saving my day. I refer to https://stackoverflow.com/a/54819859/4093741 this answer, but code not work. The error log is not very intuitive. – Da Tong Apr 22 '21 at 11:35
14

Your ObjectMapper is not managed by spring since you are creating object with new. Configure ObjectMapper as Bean in your configuration file. You need to register the DateTimeFormat with your LocalDateTimeDeserializer. Then set your Deserializer in JavaTimeModule.

@Bean
public ObjectMapper objectMapper() {
    JavaTimeModule module = new JavaTimeModule();
    LocalDateTimeDeserializer localDateTimeDeserializer = new
            LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
    ObjectMapper objectMapperObj = Jackson2ObjectMapperBuilder.json()
            .modules(module)
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .build();
    return objectMapperObj;
}

Set the above configuration in your Spring configuration.

kometen
  • 6,536
  • 6
  • 41
  • 51
Sangam Belose
  • 4,262
  • 8
  • 26
  • 48