Skip to content

Commit

Permalink
Simplify handling of LocalDate value with invalid trailing time-por…
Browse files Browse the repository at this point in the history
…tion (#211)

Fixed #212: Simplify handling of `LocalDate` value with invalid trailing time-portion; also take strict/lenient into account.
  • Loading branch information
cowtowncoder committed Mar 26, 2021
1 parent d2ddfd1 commit 8b30c9b
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@

import java.io.IOException;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;

/**
* Deserializer for Java 8 temporal {@link LocalDate}s.
Expand Down Expand Up @@ -154,11 +153,18 @@ protected LocalDate _fromString(JsonParser p, DeserializationContext ctxt,
if (format == DEFAULT_FORMATTER) {
// JavaScript by default includes time in JSON serialized Dates (UTC/ISO instant format).
if (string.length() > 10 && string.charAt(10) == 'T') {
if (string.endsWith("Z")) {
return LocalDateTime.ofInstant(Instant.parse(string), ZoneOffset.UTC).toLocalDate();
} else {
return LocalDate.parse(string, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
if (isLenient()) {
if (string.endsWith("Z")) {
return LocalDate.parse(string.substring(0, string.length() - 1),
DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
return LocalDate.parse(string, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
JavaType t = getValueType(ctxt);
return (LocalDate) ctxt.handleWeirdStringValue(t.getRawClass(),
string,
"Should not contain time component when 'strict' mode set for property or type (enable 'lenient' handling to allow)"
);
}
}
return LocalDate.parse(string, format);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ public StrictWrapperWithYearWithoutEra() { }
public StrictWrapperWithYearWithoutEra(LocalDate v) { value = v; }
}

static class StrictWrapperWithFormat {
@JsonFormat(pattern="yyyy-MM-dd",
lenient = OptBoolean.FALSE)
public LocalDate value;

public StrictWrapperWithFormat() { }
public StrictWrapperWithFormat(LocalDate v) { value = v; }
}

/*
/**********************************************************
/* Deserialization from Int array representation
Expand Down Expand Up @@ -324,6 +333,21 @@ public void testCustomFormat() throws Exception
assertEquals(28, date.getDayOfMonth());
}

@Test
public void testStrictCustomFormat() throws Exception
{
try {
/*StrictWrapperWithFormat w = */ MAPPER.readValue(
"{\"value\":\"2019-11-30\"}",
StrictWrapperWithFormat.class);
fail("Should not pass");
} catch (InvalidFormatException e) {
// 25-Mar-2021, tatu: Really bad exception message we got... but
// it is what it is
verifyException(e, "Cannot deserialize value of type `java.time.LocalDate` from String");
verifyException(e, "\"2019-11-30\"");
}
}

/*
/**********************************************************
Expand Down Expand Up @@ -402,7 +426,9 @@ public void testDeserializationCaseInsensitiveEnabledOnValue() throws Throwable
@Test
public void testDeserializationCaseInsensitiveEnabled() throws Throwable
{
ObjectMapper mapper = newMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, true);
ObjectMapper mapper = mapperBuilder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
.build();
mapper.configOverride(LocalDate.class).setFormat(JsonFormat.Value.forPattern("dd-MMM-yyyy"));
ObjectReader reader = mapper.readerFor(LocalDate.class);
String[] jsons = new String[] {"'01-Jan-2000'","'01-JAN-2000'", "'01-jan-2000'"};
Expand All @@ -414,7 +440,9 @@ public void testDeserializationCaseInsensitiveEnabled() throws Throwable
@Test
public void testDeserializationCaseInsensitiveDisabled() throws Throwable
{
ObjectMapper mapper = newMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false);
ObjectMapper mapper = mapperBuilder()
.disable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
.build();
mapper.configOverride(LocalDate.class).setFormat(JsonFormat.Value.forPattern("dd-MMM-yyyy"));
ObjectReader reader = mapper.readerFor(LocalDate.class);
expectSuccess(reader, LocalDate.of(2000, Month.JANUARY, 1), "'01-Jan-2000'");
Expand All @@ -423,7 +451,9 @@ public void testDeserializationCaseInsensitiveDisabled() throws Throwable
@Test
public void testDeserializationCaseInsensitiveDisabled_InvalidDate() throws Throwable
{
ObjectMapper mapper = newMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false);
ObjectMapper mapper = mapperBuilder()
.disable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
.build();
mapper.configOverride(LocalDate.class).setFormat(JsonFormat.Value.forPattern("dd-MMM-yyyy"));
ObjectReader reader = mapper.readerFor(LocalDate.class);
String[] jsons = new String[] {"'01-JAN-2000'", "'01-jan-2000'"};
Expand Down

This file was deleted.

3 changes: 2 additions & 1 deletion release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Modules:

2.13.0 (not yet released)

No changes since 2.12
#212: Make LocalDateDeserializer consider strict/lenient on accepting (or not)
of "time" part

2.12.3 (not yet released)

Expand Down

0 comments on commit 8b30c9b

Please sign in to comment.