I think I've read every past post on this issue and it still isn't working for me. My setup:
OpenJDK 64-Bit 11.0.14.1+1 Jackson module version 2.14.1
I have the following Jackson modules included:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
My mapper is instantiated as such having all the relevant Java dependencies above:
ObjectMapper jsonObjectMapper = new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
And my Java objects have various LocalDateTime properties defined as follows with the relevant annotations:
@JsonProperty("created")
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd")
@JsonDeserialize(using= LocalDateTimeDeserializer.class) //Tried with and without this line
private LocalDateTime created;
But when it parses I get the error:
Cannot deserialize value of type `java.time.LocalDateTime` from String "2021-10-15":
Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException)
Text '2021-10-15' could not be parsed: Unable to obtain LocalDateTime from
TemporalAccessor: {},ISO resolved to 2021-10-15 of type java.time.format.Parsed
So it seems to not be using the custom formatter. I then tried adding DateTimeFormatter directly using the following:
JavaTimeModule jtm = new JavaTimeModule();
jtm.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
ObjectMapper mapper = new ObjectMapper()
.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(jtm);
But that didn't help. What am I missing?
Since java.time
API fails to construct LocalDateTime
objects from strings with missing time part (there are obvious defaults though), you have following options:
LocalDate
-> LocalDateTime
conversion and vice versa:@JsonIgnore
private LocalDateTime created;
@JsonProperty("created")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public LocalDate getCreatedJson() {
return Optional.ofNullable(created)
.map(LocalDateTime::toLocalDate)
.orElse(null);
}
@JsonProperty("created")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public void setCreatedJson(LocalDate localDate) {
created = Optional.ofNullable(localDate)
.map(LocalDate::atStartOfDay)
.orElse(null);
}
@JsonDeserialize#converter
feature of jackson
:Which helper object (if any) is to be used to convert from Jackson-bound intermediate type (source type of converter) into actual property type (which must be same as result type of converter). This is often used for two-step deserialization; Jackson binds data into suitable intermediate type (like Tree representation), and converter then builds actual property type.
public class LocalDateTimeConverter extends StdConverter <LocalDate, LocalDateTime> {
@Override
public LocalDateTime convert(LocalDate localDate) {
return localDate.atStartOfDay();
}
}
...
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@JsonDeserialize(converter = LocalDateTimeConverter.class)
private LocalDateTime created;
public class EnhancedLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
protected EnhancedLocalDateTimeDeserializer() {
this(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
public EnhancedLocalDateTimeDeserializer(DateTimeFormatter formatter) {
super(new DateTimeFormatterBuilder()
.append(formatter)
.parseDefaulting(HOUR_OF_DAY, 0)
.parseDefaulting(MINUTE_OF_HOUR, 0)
.parseDefaulting(SECOND_OF_MINUTE, 0)
.parseDefaulting(NANO_OF_SECOND, 0)
.toFormatter());
}
protected EnhancedLocalDateTimeDeserializer(EnhancedLocalDateTimeDeserializer base, Boolean leniency) {
super(base, leniency);
}
@Override
protected EnhancedLocalDateTimeDeserializer withDateFormat(DateTimeFormatter formatter) {
return new EnhancedLocalDateTimeDeserializer(formatter);
}
@Override
protected EnhancedLocalDateTimeDeserializer withLeniency(Boolean leniency) {
return new EnhancedLocalDateTimeDeserializer(this, leniency);
}
@Override
protected EnhancedLocalDateTimeDeserializer withShape(JsonFormat.Shape shape) {
return this;
}
}
LocalDate
instead of LocalDateTime