Deserialize Java 8 LocalDateTime with JacksonMapper
Solution 1
The date time you're passing is not an ISO local date time format.
Change to
@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME)
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;
and pass the date string in the format '2011-12-03T10:15:30'.
But if you still want to pass your custom format, you just have to specify the right formatter.
Change to
@Column(name = "start_date")
@DateTimeFormat(iso = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
private LocalDateTime startDate;
I think your problem is the @DateTimeFormat has no effect at all. As the Jackson is doing the deserialization and it doesn't know anything about spring annotation, and I don't see spring scanning this annotation in the deserialization context.
Alternatively, you can try setting the formatter while registering the Java time module.
LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
Here is the test case with the deseralizer which works fine. Maybe try to get rid of that DateTimeFormat annotation altogether.
@RunWith(JUnit4.class)
public class JacksonLocalDateTimeTest {
private ObjectMapper objectMapper;
@Before
public void init() {
JavaTimeModule module = new JavaTimeModule();
LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
objectMapper = Jackson2ObjectMapperBuilder.json()
.modules(module)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}
@Test
public void test() throws IOException {
final String json = "{ \"date\": \"2016-11-08 12:00\" }";
final JsonType instance = objectMapper.readValue(json, JsonType.class);
assertEquals(LocalDateTime.parse("2016-11-08 12:00",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") ), instance.getDate());
}
}
class JsonType {
private LocalDateTime date;
public LocalDateTime getDate() {
return date;
}
public void setDate(LocalDateTime date) {
this.date = date;
}
}
Solution 2
You used wrong letter case for year in line:
@JsonFormat(pattern = "YYYY-MM-dd HH:mm")
Should be:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
With this change everything is working as expected.
Solution 3
This worked for me:
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ", shape = JsonFormat.Shape.STRING)
private LocalDateTime startDate;
Solution 4
UPDATE:
Change to:
@Column(name = "start_date")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm", iso = ISO.DATE_TIME)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime startDate;
JSON request:
{
"startDate":"2019-04-02 11:45"
}
Solution 5
There are two problems with your code:
1. Use of wrong type
LocalDateTime
does not support timezone. Given below is an overview of java.time types and you can see that the type which matches with your date-time string, 2016-12-01T23:00:00+00:00
is OffsetDateTime
because it has a zone offset of +00:00
.
Change your declaration as follows:
private OffsetDateTime startDate;
2. Use of wrong format
There are two problems with the format:
- You need to use
y
(year-of-era ) instead ofY
(week-based-year). Check this discussion to learn more about it. In fact, I recommend you useu
(year) instead ofy
(year-of-era ). Check this answer for more details on it. - You need to use
XXX
orZZZZZ
for the offset part i.e. your format should beuuuu-MM-dd'T'HH:m:ssXXX
.
Check the documentation page of DateTimeFormatter
for more details about these symbols/formats.
Demo:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String strDateTime = "2019-10-21T13:00:00+02:00";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:m:ssXXX");
OffsetDateTime odt = OffsetDateTime.parse(strDateTime, dtf);
System.out.println(odt);
}
}
Output:
2019-10-21T13:00+02:00
Learn more about the modern date-time API from Trail: Date Time.
Smajl
Software developer with focus on Java and cloud technologies.
Updated on February 16, 2022Comments
-
Smajl about 2 years
I have read several questions with answers here in SO concerning serialization and deserialization between
java.time.LocalDateTime
and JSON property but I can't seem to get it working.I have managed to configure my Spring Boot Application to return the dates in the format I desire (
YYY-MM-dd HH:mm
) but I have problems accepting values in this format in JSON.These are all the things I have done so far:
Added maven dependency for
jsr310
:<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency>
Specified
jsr310
in my main class:@EntityScan(basePackageClasses = { App.class, Jsr310JpaConverters.class })
Disabled serialization as timestamps in
application.properties
:spring.jackson.serialization.write_dates_as_timestamps=false
And this is my entity mapping for datetime:
@Column(name = "start_date") @DateTimeFormat(iso = DateTimeFormat.ISO.TIME) @JsonFormat(pattern = "YYYY-MM-dd HH:mm") private LocalDateTime startDate;
In my database, I store this date as TIMESTAMP in the following format:
2016-12-01T23:00:00+00:00
.If I access this entity via my controller, it returns the JSON with correct startDate format. When I try to post it and deserialize it though, using
YYYY-MM-dd HH:mm
format, I get the following exception:{ "timestamp": "2016-10-30T14:22:25.285+0000", "status": 400, "error": "Bad Request", "exception": "org.springframework.http.converter.HttpMessageNotReadableException", "message": "Could not read document: Can not deserialize value of type java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type java.time.format.Parsed\n at [Source: java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.time.LocalDateTime from String \"2017-01-01 20:00\": Text '2017-01-01 20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MonthOfYear=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2017, DayOfMonth=1},ISO resolved to 20:00 of type java.time.format.Parsed\n at [Source: java.io.PushbackInputStream@679a734d; line: 6, column: 16] (through reference chain: com.gigsterous.api.model.Event[\"startDate\"])", "path": "/api/events" }
I know that there are many answers concerning this topic but following them and trying for couple of hours did not help me to figure out what am I doing wrong so I would be glad if someone could point out to me what am I missing. Thanks for any input on this!
EDIT: These are all the classes involved in the process:
Repository:
@Repository public interface EventRepository extends PagingAndSortingRepository<Event, Long> { }
Controller:
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Event> createEvent(@RequestBody Event event) { return new ResponseEntity<>(eventRepo.save(event), HttpStatus.CREATED); }
My JSON request payalod:
{ "name": "Test", "startDate": "2017-01-01 20:00" }
Event:
@Entity @Table(name = "events") @Getter @Setter public class Event { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "event_id") private long id; @Column(name = "name") private String name; @Column(name = "start_date") @DateTimeFormat(iso = DateTimeFormat.ISO.TIME) @JsonFormat(pattern = "YYYY-MM-dd HH:mm") private LocalDateTime startDate; }
-
Smajl over 7 yearsI have an error with this saying I cannot convert from DateTimeFormatter to DateTimeFormat.ISO
-
s7vr over 7 yearsUse the spring equalivent for that formatter. you get the idea right ?
-
Smajl over 7 yearsThere is no such class called DateTimeFormatter in Spring. There is only DateTimeFormat (I mentioned it in my question) and i tried both formats with it (2011-12-03T10:15:30 and 2011-12-03 10:15). Neither worked
-
s7vr over 7 yearsokay. There has to be a way to pass the custom pattern to that datetimeformat annotation.
-
Smajl over 7 yearsI sure hope so, that's basically my question. I tried a lot of combinations but nothing worked so far (including @DateTimeFormat(pattern = "YYYY-MM-dd HH:mm"))
-
s7vr over 7 yearsI cant test it now. Can you try @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
-
Smajl over 7 yearsI did, see my comment above?
-
Smajl over 7 yearsNo, that does not help
-
s7vr over 7 yearsUpdated. Please take a look.
-
s7vr over 7 yearsAdded a test case with LocalDateTime Deserailizer. Please take a look.
-
Smajl over 7 yearsThank you for your effort. I made it work and will award bounty once it allows me to
-
Maciej Walkowiak over 7 yearsYYYY is just wrong. Do not overcomplicate your code and change it to yyyy
-
ankit almost 6 yearsDateTimeFormatter.ISO_LOCAL_DATE throws compile time error :
attribute value must be constant
-
pedram bashiri almost 6 yearsI had the same problem as @Smajil , for some reason DateTimeFormat and JsonFormat were not effective (even though JsonFormat is a Jackson annotation). Many ppl suggested implementing my own custom Deserializer but I didn't want to implement one Deserializer for every Json class that I had. This answer saved my day, short sweet and effective
-
Spenhouet over 5 yearsAs @ankit said this will not compile.
Attribute value must be constant
-
ankit over 5 yearsPlease answer my question: stackoverflow.com/questions/52129200/…
-
chill appreciator over 4 yearsIt just not compiles
-
Christian Meyer over 4 yearsThis is the wrong information. DateTimeFormatter is a different class than DateTimeFormat. DateTimeFormatter only has a select few enum choices, unlike DateTimeFormatter, which has a wide variety. And, by choosing DateTimeFormat.DATE_TIME, you wind up with millis despite the javadoc, which is why I am still looking for a solution.
-
Felipe Desiderati over 4 yearsThis part is worng: @DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME)
-
Anupam Jain almost 4 yearsyup.. was looking for this thing. For that exact date-time value as mentioned in question "2016-10-30T14:22:25.285+0000", this is the solution which worked for me.
-
Xelian about 2 yearsThanks this fix my parsing from 2022-02-21T17:57:53.049+0000. But what shape = JsonFormat.Shape.STRING mean?