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 98276d8a..f189ce63 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 @@ -23,8 +23,6 @@ import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.datatype.jsr310.DecimalUtils; import java.io.IOException; @@ -37,6 +35,7 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; +import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import java.util.regex.Pattern; @@ -181,23 +180,17 @@ protected InstantDeserializer withLeniency(Boolean leniency) { protected InstantDeserializer withShape(JsonFormat.Shape shape) { return this; } @SuppressWarnings("unchecked") - @Override - public JsonDeserializer createContextual(DeserializationContext ctxt, - BeanProperty property) throws JsonMappingException + @Override // @since 2.12.1 + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) { - InstantDeserializer deserializer = - (InstantDeserializer)super.createContextual(ctxt, property); - JsonFormat.Value val = findFormatOverrides(ctxt, property, handledType()); - if (val != null) { - deserializer = new InstantDeserializer<>(deserializer, val.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)); - if (val.hasLenient()) { - Boolean leniency = val.getLenient(); - if (leniency != null) { - deserializer = deserializer.withLeniency(leniency); - } - } + InstantDeserializer deser = (InstantDeserializer) super._withFormatOverrides(ctxt, + property, formatOverrides); + Boolean B = formatOverrides.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + if (!Objects.equals(B, deser._adjustToContextTZOverride)) { + return new InstantDeserializer(deser, B); } - return deserializer; + return deser; } @SuppressWarnings("unchecked") 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 ca18a72e..65ea24d9 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 @@ -101,53 +101,70 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); + return (format == null) ? this : _withFormatOverrides(ctxt, property, format); + } + + /** + * @param ctxt Active deserialization context + * @param property (optional) Property on which this deserializer is used, or {@code null} + * for root value + * @param formatOverrides Format overrides to use (non-null) + * + * @return Either this deserializer as is, or newly constructed variant if created + * for different configuration + * + * @since 2.12.1 + */ + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) + { JSR310DateTimeDeserializerBase deser = this; - if (format != null) { - // 17-Aug-2019, tatu: For 2.10 let's start considering leniency/strictness too - if (format.hasLenient()) { - Boolean leniency = format.getLenient(); - if (leniency != null) { - deser = deser.withLeniency(leniency); - } + + // 17-Aug-2019, tatu: For 2.10 let's start considering leniency/strictness too + if (formatOverrides.hasLenient()) { + Boolean leniency = formatOverrides.getLenient(); + if (leniency != null) { + deser = deser.withLeniency(leniency); } - if (format.hasPattern()) { - final String pattern = format.getPattern(); - final Locale locale = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); - if (acceptCaseInsensitiveValues(ctxt, format)) { - builder.parseCaseInsensitive(); - } - builder.appendPattern(pattern); - DateTimeFormatter df; - if (locale == null) { - df = builder.toFormatter(); - } else { - df = builder.toFormatter(locale); - } - - // [#148]: allow strict parsing - if (!deser.isLenient()) { - df = df.withResolverStyle(ResolverStyle.STRICT); - } - - // [#69]: For instant serializers/deserializers we need to configure the formatter with - //a time zone picked up from JsonFormat annotation, otherwise serialization might not work - if (format.hasTimeZone()) { - df = df.withZone(format.getTimeZone().toZoneId()); - } - deser = deser.withDateFormat(df); + } + if (formatOverrides.hasPattern()) { + final String pattern = formatOverrides.getPattern(); + final Locale locale = formatOverrides.hasLocale() ? formatOverrides.getLocale() : ctxt.getLocale(); + DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); + if (acceptCaseInsensitiveValues(ctxt, formatOverrides)) { + builder.parseCaseInsensitive(); } - // [#58]: For LocalDate deserializers we need to configure the formatter with - //a shape picked up from JsonFormat annotation, to decide if the value is EpochSeconds - JsonFormat.Shape shape = format.getShape(); - if (shape != null && shape != _shape) { - deser = deser.withShape(shape); + builder.appendPattern(pattern); + DateTimeFormatter df; + if (locale == null) { + df = builder.toFormatter(); + } else { + df = builder.toFormatter(locale); } - // any use for TimeZone? + + // [#148]: allow strict parsing + if (!deser.isLenient()) { + df = df.withResolverStyle(ResolverStyle.STRICT); + } + + // [#69]: For instant serializers/deserializers we need to configure the formatter with + //a time zone picked up from JsonFormat annotation, otherwise serialization might not work + if (formatOverrides.hasTimeZone()) { + df = df.withZone(formatOverrides.getTimeZone().toZoneId()); + } + deser = deser.withDateFormat(df); } + // [#58]: For LocalDate deserializers we need to configure the formatter with + //a shape picked up from JsonFormat annotation, to decide if the value is EpochSeconds + JsonFormat.Shape shape = formatOverrides.getShape(); + if (shape != null && shape != _shape) { + deser = deser.withShape(shape); + } + // any use for TimeZone? + return deser; } - + private boolean acceptCaseInsensitiveValues(DeserializationContext ctxt, JsonFormat.Value format) { Boolean enabled = format.getFeature( Feature.ACCEPT_CASE_INSENSITIVE_VALUES); diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 7c051772..f47964b2 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -8,6 +8,12 @@ Modules: === Releases === ------------------------------------------------------------------------ +2.12.1 (not yet released) + +#196: `@JsonFormat` overriden features don't apply when there are no other + options while deserializing ZonedDateTime + (reported, fix contributed by Maciej D) + 2.12.0 (29-Nov-2020) #94: Deserialization of timestamps with UTC timezone to LocalDateTime