Skip to content

Commit 48ba124

Browse files
kupcicowtowncoder
authored andcommitted
Fix #114: make empty String fail to deserialize to null under "strict" mode
1 parent 08b9106 commit 48ba124

File tree

4 files changed

+125
-2
lines changed

4 files changed

+125
-2
lines changed

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserializer.java

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ public LocalDate deserialize(JsonParser parser, DeserializationContext context)
7979
if (parser.hasToken(JsonToken.VALUE_STRING)) {
8080
String string = parser.getText().trim();
8181
if (string.length() == 0) {
82+
if (!isLenient()) {
83+
return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
84+
}
8285
return null;
8386
}
8487
// as per [datatype-jsr310#37], only check for optional (and, incorrect...) time marker 'T'

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserializer.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,21 @@ public LocalDateTimeDeserializer(DateTimeFormatter formatter) {
5252
super(LocalDateTime.class, formatter);
5353
}
5454

55+
/**
56+
* Since 2.10
57+
*/
58+
protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, Boolean leniency) {
59+
super(base, leniency);
60+
}
61+
5562
@Override
5663
protected LocalDateTimeDeserializer withDateFormat(DateTimeFormatter formatter) {
5764
return new LocalDateTimeDeserializer(formatter);
5865
}
5966

60-
// !!! TODO: lenient vs strict?
6167
@Override
6268
protected LocalDateTimeDeserializer withLeniency(Boolean leniency) {
63-
return this;
69+
return new LocalDateTimeDeserializer(this, leniency);
6470
}
6571

6672
@Override
@@ -69,6 +75,9 @@ public LocalDateTime deserialize(JsonParser parser, DeserializationContext conte
6975
if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
7076
String string = parser.getText().trim();
7177
if (string.length() == 0) {
78+
if (!isLenient()) {
79+
return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
80+
}
7281
return null;
7382
}
7483

datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java

+55
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import java.time.Month;
77
import java.time.ZoneOffset;
88
import java.time.temporal.Temporal;
9+
import java.util.Map;
910

11+
import com.fasterxml.jackson.core.type.TypeReference;
1012
import org.junit.Test;
1113

1214
import static org.junit.Assert.assertEquals;
@@ -27,6 +29,7 @@ public class LocalDateDeserTest extends ModuleTestBase
2729
{
2830
private final ObjectMapper MAPPER = newMapper();
2931
private final ObjectReader READER = MAPPER.readerFor(LocalDate.class);
32+
private final TypeReference<Map<String, LocalDate>> MAP_TYPE_REF = new TypeReference<Map<String, LocalDate>>() { };
3033

3134
final static class Wrapper {
3235
@JsonFormat(pattern="yyyy_MM_dd'T'HH:mmZ",
@@ -159,6 +162,58 @@ public void testStricDeserializeFromInt() throws Exception
159162
// be content with just one...
160163
}
161164

165+
/*
166+
/**********************************************************
167+
/* Tests for empty string handling
168+
*/
169+
/**********************************************************
170+
*/
171+
172+
@Test
173+
public void testLenientDeserializeFromEmptyString() throws Exception {
174+
175+
String key = "date";
176+
ObjectMapper mapper = newMapper();
177+
ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF);
178+
179+
String dateValAsNullStr = null;
180+
String dateValAsEmptyStr = "";
181+
182+
String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr));
183+
Map<String, LocalDate> actualMapFromNullStr = objectReader.readValue(valueFromNullStr);
184+
LocalDate actualDateFromNullStr = actualMapFromNullStr.get(key);
185+
assertNull(actualDateFromNullStr);
186+
187+
String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr));
188+
Map<String, LocalDate> actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr);
189+
LocalDate actualDateFromEmptyStr = actualMapFromEmptyStr.get(key);
190+
assertEquals("empty string failed to deserialize to null with lenient setting",actualDateFromNullStr, actualDateFromEmptyStr);
191+
}
192+
193+
@Test( expected = MismatchedInputException.class)
194+
public void testStrictDeserializFromEmptyString() throws Exception {
195+
196+
final String key = "date";
197+
final ObjectMapper mapper = mapperBuilder()
198+
.withConfigOverride(LocalDate.class,
199+
c -> c.setFormat(JsonFormat.Value.forLeniency(false))
200+
)
201+
.build();
202+
final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF);
203+
final String dateValAsNullStr = null;
204+
205+
// even with strict, null value should be deserialized without throwing an exception
206+
String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr));
207+
Map<String, LocalDate> actualMapFromNullStr = objectReader.readValue(valueFromNullStr);
208+
assertNull(actualMapFromNullStr.get(key));
209+
210+
String dateValAsEmptyStr = "";
211+
// TODO: nothing stops us from writing an empty string, maybe there should be a check there too?
212+
String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", dateValAsEmptyStr));
213+
// with strict, deserializing an empty string is not permitted
214+
objectReader.readValue(valueFromEmptyStr);
215+
}
216+
162217
/*
163218
/**********************************************************
164219
/* Tests for alternate array handling

datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserTest.java

+56
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
import java.time.temporal.Temporal;
2525
import java.util.Calendar;
2626
import java.util.Date;
27+
import java.util.Map;
2728
import java.util.TimeZone;
2829

30+
import com.fasterxml.jackson.annotation.JsonFormat;
2931
import com.fasterxml.jackson.core.JsonParser;
3032
import com.fasterxml.jackson.core.JsonToken;
3133

34+
import com.fasterxml.jackson.core.type.TypeReference;
3235
import com.fasterxml.jackson.databind.*;
3336
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
3437
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
@@ -46,6 +49,7 @@ public class LocalDateTimeDeserTest
4649
{
4750
private final static ObjectMapper MAPPER = newMapper();
4851
private final static ObjectReader READER = MAPPER.readerFor(LocalDateTime.class);
52+
private final TypeReference<Map<String, LocalDateTime>> MAP_TYPE_REF = new TypeReference<Map<String, LocalDateTime>>() { };
4953

5054
/*
5155
/**********************************************************
@@ -174,6 +178,58 @@ public void testBadDeserializationAsString01() throws Throwable
174178
}
175179
}
176180

181+
/*
182+
/**********************************************************
183+
/* Tests for empty string handling
184+
*/
185+
/**********************************************************
186+
*/
187+
188+
@Test
189+
public void testLenientDeserializeFromEmptyString() throws Exception {
190+
191+
String key = "datetime";
192+
ObjectMapper mapper = newMapper();
193+
ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF);
194+
195+
String dateValAsNullStr = null;
196+
String dateValAsEmptyStr = "";
197+
198+
String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr));
199+
Map<String, LocalDateTime> actualMapFromNullStr = objectReader.readValue(valueFromNullStr);
200+
LocalDateTime actualDateFromNullStr = actualMapFromNullStr.get(key);
201+
assertNull(actualDateFromNullStr);
202+
203+
String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr));
204+
Map<String, LocalDateTime> actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr);
205+
LocalDateTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key);
206+
assertEquals("empty string failed to deserialize to null with lenient setting",actualDateFromNullStr, actualDateFromEmptyStr);
207+
}
208+
209+
@Test( expected = MismatchedInputException.class)
210+
public void testStrictDeserializFromEmptyString() throws Exception {
211+
212+
final String key = "datetime";
213+
final ObjectMapper mapper = mapperBuilder()
214+
.withConfigOverride(LocalDateTime.class,
215+
c -> c.setFormat(JsonFormat.Value.forLeniency(false))
216+
)
217+
.build();
218+
final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF);
219+
final String dateValAsNullStr = null;
220+
221+
// even with strict, null value should be deserialized without throwing an exception
222+
String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr));
223+
Map<String, LocalDateTime> actualMapFromNullStr = objectReader.readValue(valueFromNullStr);
224+
assertNull(actualMapFromNullStr.get(key));
225+
226+
String dateValAsEmptyStr = "";
227+
// TODO: nothing stops us from writing an empty string, maybe there should be a check there too?
228+
String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", dateValAsEmptyStr));
229+
// with strict, deserializing an empty string is not permitted
230+
objectReader.readValue(valueFromEmptyStr);
231+
}
232+
177233
/*
178234
/**********************************************************
179235
/* Tests for alternate array handling

0 commit comments

Comments
 (0)