Skip to content

Commit

Permalink
Minor improvements to coercion checking for java.util.Date/Calendar
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 11, 2020
1 parent 8af0abd commit 6f7e951
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ public CoercionAction findCoercion(DeserializationConfig config,
// Since coercion of scalar must be enabled (see check above), allow empty-string
// coercions by default even without this setting
if (classicScalar
|| (targetType == LogicalType.DateTime)
// Default for setting is false
|| config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
return CoercionAction.AsNull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.type.LogicalType;
import com.fasterxml.jackson.databind.util.ClassUtil;
Expand Down Expand Up @@ -191,7 +193,15 @@ protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
if (p.hasToken(JsonToken.VALUE_STRING)) {
String str = p.getText().trim();
if (str.length() == 0) {
return (Date) getEmptyValue(ctxt);
final CoercionAction act = _checkFromStringCoercion(ctxt, str);
switch (act) { // note: Fail handled above
case AsEmpty:
return new java.util.Date(0L);
case AsNull:
case TryConvert:
default:
}
return null;
}
synchronized (_customFormat) {
try {
Expand Down Expand Up @@ -245,6 +255,13 @@ protected CalendarDeserializer withDateFormat(DateFormat df, String formatString
return new CalendarDeserializer(this, df, formatString);
}

@Override // since 2.12
public Object getEmptyValue(DeserializationContext ctxt) {
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis(0L);
return cal;
}

@Override
public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
Expand Down Expand Up @@ -290,6 +307,11 @@ public DateDeserializer(DateDeserializer base, DateFormat df, String formatStrin
protected DateDeserializer withDateFormat(DateFormat df, String formatString) {
return new DateDeserializer(this, df, formatString);
}

@Override // since 2.12
public Object getEmptyValue(DeserializationContext ctxt) {
return new Date(0L);
}

@Override
public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Expand All @@ -313,7 +335,12 @@ public SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String format
protected SqlDateDeserializer withDateFormat(DateFormat df, String formatString) {
return new SqlDateDeserializer(this, df, formatString);
}


@Override // since 2.12
public Object getEmptyValue(DeserializationContext ctxt) {
return new java.sql.Date(0L);
}

@Override
public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Date d = _parseDate(p, ctxt);
Expand All @@ -339,7 +366,12 @@ public TimestampDeserializer(TimestampDeserializer src, DateFormat df, String fo
protected TimestampDeserializer withDateFormat(DateFormat df, String formatString) {
return new TimestampDeserializer(this, df, formatString);
}


@Override // since 2.12
public Object getEmptyValue(DeserializationContext ctxt) {
return new Timestamp(0L);
}

@Override
public java.sql.Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -748,8 +748,20 @@ protected java.util.Date _parseDate(String value, DeserializationContext ctxt)
{
try {
// Take empty Strings to mean 'empty' Value, usually 'null':
if (_isEmptyOrTextualNull(value)) {
return (java.util.Date) getNullValue(ctxt);
if (value.length() == 0) {
final CoercionAction act = _checkFromStringCoercion(ctxt, value);
switch (act) { // note: Fail handled above
case AsEmpty:
return new java.util.Date(0L);
case AsNull:
case TryConvert:
default:
}
return null;
}
// 10-Jun-2020, tatu: Legacy handling from pre-2.12... should we still have it?
if (_hasTextualNull(value)) {
return null;
}
return ctxt.parseDate(value);
} catch (IllegalArgumentException iae) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.UUID;
Expand Down Expand Up @@ -188,7 +191,7 @@ public void testUUIDCoercions() throws Exception
_testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, UUID.class);
_testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, UUID.class);

// but allow separate "empty" value is specifically requeted
// but allow separate "empty" value is specifically requested
_testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, UUID.class,
new UUID(0L, 0L));

Expand Down Expand Up @@ -220,6 +223,29 @@ private void _checkEmptyStringBuilder(StringBuilder sb) {
assertEquals(0, sb.length());
}

// Date, Calendar also included here for convenience

public void testLegacyDateTimeCoercions() throws Exception
{
// Coerce to `null` both by default, "TryConvert" and explicit
_testScalarEmptyToNull(DEFAULT_MAPPER, Calendar.class);
_testScalarEmptyToNull(DEFAULT_MAPPER, Date.class);
_testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Calendar.class);
_testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Date.class);
_testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Calendar.class);
_testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Date.class);

// but allow separate "empty" value is specifically requested
Calendar emptyCal = new GregorianCalendar();
emptyCal.setTimeInMillis(0L);
// _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Calendar.class, emptyCal);
_testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Date.class, new Date(0L));

// allow forcing failure, too
_verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Calendar.class);
_verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Date.class);
}

/*
/********************************************************
/* Second-level test helper methods
Expand All @@ -235,6 +261,9 @@ private void _testScalarEmptyToEmpty(ObjectMapper mapper,
Class<?> target, Object emptyValue) throws Exception
{
Object result = mapper.readerFor(target).readValue(JSON_EMPTY);
if (result == null) {
fail("Expected empty, non-null value for "+target.getName()+", got null");
}
assertEquals(emptyValue, result);
}

Expand Down

0 comments on commit 6f7e951

Please sign in to comment.