From 6c7fc6ab74118727fcaba8c38e5fe98fc8b59c26 Mon Sep 17 00:00:00 2001 From: michaelok Date: Tue, 24 Sep 2019 01:37:21 -0500 Subject: [PATCH 1/2] per code review, add test specifically from issue where default mapper is used --- .../jsr310/deser/InstantDeserializer.java | 5 +++- .../deser/JSR310DateTimeDeserializerBase.java | 24 +++++++++++++++++ .../jsr310/deser/LocalDateDeserializer.java | 18 ++++++++++--- .../deser/LocalDateTimeDeserializer.java | 4 +++ .../jsr310/deser/LocalTimeDeserializer.java | 4 +++ .../jsr310/deser/MonthDayDeserializer.java | 4 +++ .../jsr310/deser/OffsetTimeDeserializer.java | 4 +++ .../jsr310/deser/YearDeserializer.java | 4 +++ .../jsr310/deser/YearMonthDeserializer.java | 4 +++ .../jsr310/deser/LocalDateDeserTest.java | 26 ++++++++++++++++++- 10 files changed, 92 insertions(+), 5 deletions(-) diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java index e912fc60..c4ede2e0 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java @@ -160,7 +160,10 @@ protected InstantDeserializer withDateFormat(DateTimeFormatter dtf) { protected InstantDeserializer withLeniency(Boolean leniency) { return this; } - + + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @SuppressWarnings("unchecked") @Override public T deserialize(JsonParser parser, DeserializationContext context) throws IOException diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java index 91059754..12f65ffa 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java @@ -34,10 +34,17 @@ public abstract class JSR310DateTimeDeserializerBase */ protected final boolean _isLenient; + /** + * Flag that indicates the {@link JsonFormat.Shape} annotation. + * + */ + protected final JsonFormat.Shape _shape; + protected JSR310DateTimeDeserializerBase(Class supportedType, DateTimeFormatter f) { super(supportedType); _formatter = f; _isLenient = true; + _shape = null; } protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, @@ -45,17 +52,28 @@ protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, super(base); _formatter = f; _isLenient = base._isLenient; + _shape = base._shape; } protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, Boolean leniency) { super(base); _formatter = base._formatter; + _shape = base._shape; _isLenient = !Boolean.FALSE.equals(leniency); } + protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, + JsonFormat.Shape shape) { + super(base); + _formatter = base._formatter; + _shape = shape; + _isLenient = base._isLenient; + } + protected abstract JSR310DateTimeDeserializerBase withDateFormat(DateTimeFormatter dtf); protected abstract JSR310DateTimeDeserializerBase withLeniency(Boolean leniency); + protected abstract JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape); @Override public JsonDeserializer createContextual(DeserializationContext ctxt, @@ -92,6 +110,12 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, deser = deser.withLeniency(leniency); } } + //if (format.hasShape()) { + JsonFormat.Shape shape = format.getShape(); + if (shape != null) { + deser = deser.withShape(shape); + } + //} // any use for TimeZone? } return deser; diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserializer.java index 0f1d834c..5441f1fc 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserializer.java @@ -24,6 +24,7 @@ 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; @@ -61,6 +62,13 @@ protected LocalDateDeserializer(LocalDateDeserializer base, Boolean leniency) { super(base, leniency); } + /** + * Since 2.9.10 + */ + protected LocalDateDeserializer(LocalDateDeserializer base, JsonFormat.Shape shape) { + super(base, shape); + } + @Override protected LocalDateDeserializer withDateFormat(DateTimeFormatter dtf) { return new LocalDateDeserializer(this, dtf); @@ -71,6 +79,9 @@ protected LocalDateDeserializer withLeniency(Boolean leniency) { return new LocalDateDeserializer(this, leniency); } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return new LocalDateDeserializer(this, shape); } + @Override public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException { @@ -134,10 +145,11 @@ public LocalDate deserialize(JsonParser parser, DeserializationContext context) } // 06-Jan-2018, tatu: Is this actually safe? Do users expect such coercion? if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { - if (!isLenient()) { - return _failForNotLenient(parser, context, JsonToken.VALUE_STRING); + // issue 58 - also check for NUMBER_INT, which needs to be specified when serializing. + if (_shape == JsonFormat.Shape.NUMBER_INT || isLenient()) { + return LocalDate.ofEpochDay(parser.getLongValue()); } - return LocalDate.ofEpochDay(parser.getLongValue()); + return _failForNotLenient(parser, context, JsonToken.VALUE_STRING); } return _handleUnexpectedToken(context, parser, "Expected array or string."); } diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserializer.java index 794c280a..6cbb98b6 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateTimeDeserializer.java @@ -23,6 +23,7 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.JsonTokenId; @@ -66,6 +67,9 @@ protected LocalDateTimeDeserializer withLeniency(Boolean leniency) { return new LocalDateTimeDeserializer(this, leniency); } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalTimeDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalTimeDeserializer.java index fe515fe7..fa514694 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalTimeDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalTimeDeserializer.java @@ -21,6 +21,7 @@ import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; @@ -56,6 +57,9 @@ protected LocalTimeDeserializer withLeniency(Boolean leniency) { return this; } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public LocalTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/MonthDayDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/MonthDayDeserializer.java index 6fbcbb6e..28a48219 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/MonthDayDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/MonthDayDeserializer.java @@ -5,6 +5,7 @@ import java.time.MonthDay; import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; @@ -32,6 +33,9 @@ protected MonthDayDeserializer withLeniency(Boolean leniency) { return this; } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public MonthDay deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetTimeDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetTimeDeserializer.java index f9d31a72..acf5edeb 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetTimeDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/OffsetTimeDeserializer.java @@ -22,6 +22,7 @@ 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.*; @@ -53,6 +54,9 @@ protected OffsetTimeDeserializer withLeniency(Boolean leniency) { return this; } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public OffsetTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearDeserializer.java index 3b3ae6a3..2284a6b8 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearDeserializer.java @@ -16,6 +16,7 @@ package com.fasterxml.jackson.datatype.jsr310.deser; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; @@ -54,6 +55,9 @@ protected YearDeserializer withLeniency(Boolean leniency) { return this; } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public Year deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearMonthDeserializer.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearMonthDeserializer.java index 32e6e780..0080b6b4 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearMonthDeserializer.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/YearMonthDeserializer.java @@ -21,6 +21,7 @@ import java.time.YearMonth; import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -57,6 +58,9 @@ protected YearMonthDeserializer withLeniency(Boolean leniency) { return this; } + @Override + protected JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape) { return this; } + @Override public YearMonth deserialize(JsonParser parser, DeserializationContext context) throws IOException { diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java index 8bc7a943..024b617e 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java @@ -161,7 +161,7 @@ public void testLenientDeserializeFromInt() throws Exception // But with alternate setting, not so @Test - public void testStricDeserializeFromInt() throws Exception + public void testStrictDeserializeFromInt() throws Exception { ObjectMapper mapper = mapperBuilder() .withConfigOverride(LocalDate.class, @@ -180,6 +180,30 @@ public void testStricDeserializeFromInt() throws Exception // be content with just one... } + @Test + public void testLenientDeserializeFromNumberInt() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER_INT))) + .build(); + + assertEquals("The value is not correct.", LocalDate.of(1970, Month.MAY, 04), + mapper.readValue("123", LocalDate.class)); + } + + @Test + public void testStrictDeserializeFromNumberInt() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER_INT))) + .build(); + + assertEquals("The value is not correct.", LocalDate.of(1970, Month.MAY, 04), + mapper.readValue("123", LocalDate.class)); + } + /* /********************************************************** /* Tests for empty string handling From d10bcce9c0df24b465341deb56b94fada7aa1675 Mon Sep 17 00:00:00 2001 From: michaelok Date: Tue, 24 Sep 2019 23:34:50 -0500 Subject: [PATCH 2/2] fixes per code review --- .../deser/JSR310DateTimeDeserializerBase.java | 26 +++++++++------- .../jsr310/deser/LocalDateDeserTest.java | 30 +++++++++++++++++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java index 12f65ffa..cdabdb63 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat.Feature; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.BeanProperty; @@ -35,10 +36,17 @@ public abstract class JSR310DateTimeDeserializerBase protected final boolean _isLenient; /** - * Flag that indicates the {@link JsonFormat.Shape} annotation. + * Setting that indicates the {@Link JsonFormat.Shape} specified for this deserializer + * as a {@link JsonFormat.Shape} annotation on property or class, or due to per-type + * "config override", or from global settings: + * If Shape is NUMBER_INT, the input value is considered to be epoch days. If not a + * NUMBER_INT, and the deserializer was not specified with the leniency setting of true, + * then an exception will be thrown. + * @see [jackson-modules-java8#58] for more info * + * @since 2.10 */ - protected final JsonFormat.Shape _shape; + protected final Shape _shape; protected JSR310DateTimeDeserializerBase(Class supportedType, DateTimeFormatter f) { super(supportedType); @@ -64,7 +72,7 @@ protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, } protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, - JsonFormat.Shape shape) { + Shape shape) { super(base); _formatter = base._formatter; _shape = shape; @@ -73,7 +81,7 @@ protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, protected abstract JSR310DateTimeDeserializerBase withDateFormat(DateTimeFormatter dtf); protected abstract JSR310DateTimeDeserializerBase withLeniency(Boolean leniency); - protected abstract JSR310DateTimeDeserializerBase withShape(JsonFormat.Shape shape); + protected abstract JSR310DateTimeDeserializerBase withShape(Shape shape); @Override public JsonDeserializer createContextual(DeserializationContext ctxt, @@ -110,12 +118,10 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, deser = deser.withLeniency(leniency); } } - //if (format.hasShape()) { - JsonFormat.Shape shape = format.getShape(); - if (shape != null) { - deser = deser.withShape(shape); - } - //} + Shape shape = format.getShape(); + if (shape != null && shape != _shape) { + deser = deser.withShape(shape); + } // any use for TimeZone? } return deser; diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java index 024b617e..8ae17a23 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java @@ -44,7 +44,15 @@ final static class Wrapper { public Wrapper() { } public Wrapper(LocalDate v) { value = v; } } - + + final static class ShapeWrapper { + @JsonFormat(shape=JsonFormat.Shape.NUMBER_INT) + public LocalDate date; + + public ShapeWrapper() { } + public ShapeWrapper(LocalDate v) { date = v; } + } + /* /********************************************************** /* Deserialization from Int array representation @@ -197,11 +205,27 @@ public void testStrictDeserializeFromNumberInt() throws Exception { ObjectMapper mapper = newMapperBuilder() .withConfigOverride(LocalDate.class, - o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER_INT))) + c -> c.setFormat(JsonFormat.Value.forLeniency(false)) + ) .build(); + ShapeWrapper w = mapper.readValue("{\"date\":123}", ShapeWrapper.class); + LocalDate localDate = w.date; + assertEquals("The value is not correct.", LocalDate.of(1970, Month.MAY, 04), - mapper.readValue("123", LocalDate.class)); + localDate); + } + + @Test(expected = MismatchedInputException.class) + public void testStrictDeserializeFromString() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + c -> c.setFormat(JsonFormat.Value.forLeniency(false)) + ) + .build(); + + mapper.readValue("{\"value\":123}", Wrapper.class); } /*