Deserialize Java 8 Localdatetime with Jacksonmapper

Deserialize Java 8 LocalDateTime with JacksonMapper

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

serialize/deserialize java 8 java.time with Jackson JSON mapper

There's no need to use custom serializers/deserializers here. Use jackson-modules-java8's datetime module:

Datatype module to make Jackson recognize Java 8 Date & Time API data types (JSR-310).

This module adds support for quite a few classes:

  • Duration
  • Instant
  • LocalDateTime
  • LocalDate
  • LocalTime
  • MonthDay
  • OffsetDateTime
  • OffsetTime
  • Period
  • Year
  • YearMonth
  • ZonedDateTime
  • ZoneId
  • ZoneOffset

Deserialize LocalDateTime from String with Jackson

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.

Jackson deserialize LocalDateTime

Replace the pattern as pattern = DateTimeFormatter.ISO_OFFSET_DATE_TIME

Your date-time string is already in the format specified by DateTimeFormatter.ISO_OFFSET_DATE_TIME which is the default format used by OffsetDateTime#parse.

Demo:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;

public class Main {
public static void main(String[] args) {
String dateTimeStr = "2020-10-01T15:30:27.4394205+03:00";

// Parse the date-time string into OffsetDateTime
OffsetDateTime odt = OffsetDateTime.parse(dateTimeStr);
System.out.println(odt);

// LocalDateTime from OffsetDateTime
LocalDateTime ldt = odt.toLocalDateTime();
System.out.println(ldt);
}
}

Output:

2020-10-01T15:30:27.439420500+03:00
2020-10-01T15:30:27.439420500

Spring boot: JSON deserialize date and time with time zone to LocalDateTime

There are two problems with your code:

1. Use of wrong type

Cannot deserialize value of type java.time.LocalDateTime from String
"2019-10-21T13:00:00+02:00": Failed to deserialize
java.time.LocalDateTime: (java.time.format.DateTimeParseException)
Text '2019-10-21T13:00:00+02:00' could not be parsed, unparsed text
found at index 19

If you analyze the error message, you will find that it is clearly telling you that there is some problem at index 19.

2019-10-21T13:00:00+02:00
// index 19 ----> ^

And, the problem is that 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 is OffsetDateTime because it has a zone offset of +02:00 hours.

Sample Image

Change your declaration as follows:

private OffsetDateTime startTime;

2. Use of wrong format

You need to use XXX for the offset part i.e. your format should be uuuu-MM-dd'T'HH:m:ssXXX. If you want to stick to Z, you need to use ZZZZZ. Check the documentation page of DateTimeFormatter for more details.

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.

Also related, RFC3339 - Date and Time on the Internet: Timestamps

This document defines a date and time format for use in Internet

protocols that is a profile of the ISO 8601 standard for

representation of dates and times using the Gregorian calendar.

LocalDateTime - deserialization with LocalDateTime.parse

Vanilla Jackson doesn't have a way to deserialize a LocalDateTime object from any JSON string value.

You have a few options. You can create and register your own JsonDeserializer which will use LocalDateTime#parse.

class ParseDeserializer extends StdDeserializer<LocalDateTime> {
public ParseDeserializer() {
super(LocalDateTime.class);
}

@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return LocalDateTime.parse(p.getValueAsString()); // or overloaded with an appropriate format
}
}
...
@JsonSerialize(using = ToStringSerializer.class)
@JsonDeserialize(using = ParseDeserializer.class)
private LocalDateTime initiationDate;

Or you can add Jackson's java.time extension to your classpath and register the appropriate Module with your ObjectMapper.

objectMapper.registerModule(new JavaTimeModule());

and let Jackson do the conversion for you. Internally, this uses LocalDateTime#parse with one of the standard formats. Fortunately, it supports values like

2016-05-11T17:32:20.897

out of the box.

Java 8 LocalDate Jackson format

I was never able to get this to work simple using annotations. To get it to work, I created a ContextResolver for ObjectMapper, then I added the JSR310Module (update: now it is JavaTimeModule instead), along with one more caveat, which was the need to set write-date-as-timestamp to false. See more at the documentation for the JSR310 module. Here's an example of what I used.

Dependency

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

Note: One problem I faced with this is that the jackson-annotation version pulled in by another dependency, used version 2.3.2, which cancelled out the 2.4 required by the jsr310. What happened was I got a NoClassDefFound for ObjectIdResolver, which is a 2.4 class. So I just needed to line up the included dependency versions

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper MAPPER;

public ObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
// Now you should use JavaTimeModule instead
MAPPER.registerModule(new JSR310Module());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}

@Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}

Resource class

@Path("person")
public class LocalDateResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getPerson() {
Person person = new Person();
person.birthDate = LocalDate.now();
return Response.ok(person).build();
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createPerson(Person person) {
return Response.ok(
DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
}

public static class Person {
public LocalDate birthDate;
}
}

Test

curl -v http://localhost:8080/api/person

Result: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person

Result: 2015-03-01


See also here for JAXB solution.

UPDATE

The JSR310Module is deprecated as of version 2.7 of Jackson. Instead, you should register the module JavaTimeModule. It is still the same dependency.

Deserialize '2021-09-24 00:00:00' date format via Jackson when using lombok @Builder

Gets fixed by:

@Getter
@Builder
@JsonDeserialize(builder = AdGroup.AdGroupBuilder.class)
public class AdGroup {

public static class AdGroupBuilder {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime uploadDate;
}

@JsonProperty("upload_date")
private LocalDateTime uploadDate;

}


Related Topics



Leave a reply



Submit