From 9744808d49359a0e8f1344e889845534daac8960 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 01:14:58 +0100 Subject: [PATCH 01/21] start of move --- .../databind/datetime/JavaTimeFeature.java | 77 +++ .../databind/datetime/JavaTimeModule.java | 252 ++++++++ .../datetime/deser/DurationDeserializer.java | 214 +++++++ .../datetime/deser/InstantDeserializer.java | 565 ++++++++++++++++++ .../deser/JSR310DateTimeDeserializerBase.java | 175 ++++++ .../deser/JSR310DeserializerBase.java | 240 ++++++++ .../JSR310StringParsableDeserializer.java | 186 ++++++ .../deser/JavaTimeDeserializerModifier.java | 28 + .../datetime/deser/LocalDateDeserializer.java | 215 +++++++ .../deser/LocalDateTimeDeserializer.java | 254 ++++++++ .../datetime/deser/LocalTimeDeserializer.java | 196 ++++++ .../datetime/deser/MonthDayDeserializer.java | 129 ++++ .../deser/OffsetTimeDeserializer.java | 195 ++++++ .../deser/OneBasedMonthDeserializer.java | 48 ++ .../datetime/deser/YearDeserializer.java | 132 ++++ .../datetime/deser/YearMonthDeserializer.java | 146 +++++ .../deser/key/DurationKeyDeserializer.java | 27 + .../deser/key/InstantKeyDeserializer.java | 28 + .../deser/key/Jsr310KeyDeserializer.java | 41 ++ .../deser/key/LocalDateKeyDeserializer.java | 28 + .../key/LocalDateTimeKeyDeserializer.java | 29 + .../deser/key/LocalTimeKeyDeserializer.java | 29 + .../deser/key/MonthDayKeyDeserializer.java | 40 ++ .../key/OffsetDateTimeKeyDeserializer.java | 28 + .../deser/key/OffsetTimeKeyDeserializer.java | 29 + .../deser/key/PeriodKeyDeserializer.java | 27 + .../deser/key/YearKeyDeserializer.java | 29 + .../deser/key/YearMonthKeyDeserializer.java | 37 ++ .../deser/key/ZoneIdKeyDeserializer.java | 27 + .../deser/key/ZoneOffsetKeyDeserializer.java | 27 + .../key/ZonedDateTimeKeyDeserializer.java | 28 + .../datetime/ser/DurationSerializer.java | 185 ++++++ .../datetime/ser/InstantSerializer.java | 65 ++ .../datetime/ser/InstantSerializerBase.java | 147 +++++ .../ser/JSR310FormattedSerializerBase.java | 258 ++++++++ .../datetime/ser/JSR310SerializerBase.java | 40 ++ .../ser/JavaTimeSerializerModifier.java | 28 + .../datetime/ser/LocalDateSerializer.java | 132 ++++ .../datetime/ser/LocalDateTimeSerializer.java | 133 +++++ .../datetime/ser/LocalTimeSerializer.java | 153 +++++ .../datetime/ser/MonthDaySerializer.java | 104 ++++ .../ser/OffsetDateTimeSerializer.java | 43 ++ .../datetime/ser/OffsetTimeSerializer.java | 122 ++++ .../datetime/ser/OneBasedMonthSerializer.java | 33 + .../datetime/ser/YearMonthSerializer.java | 122 ++++ .../databind/datetime/ser/YearSerializer.java | 94 +++ .../datetime/ser/ZoneIdSerializer.java | 33 + .../datetime/ser/ZonedDateTimeSerializer.java | 107 ++++ .../ser/key/ZonedDateTimeKeySerializer.java | 49 ++ .../databind/datetime/util/DecimalUtils.java | 136 +++++ .../datetime/util/DurationUnitConverter.java | 82 +++ 51 files changed, 5572 insertions(+) create mode 100644 src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java create mode 100644 src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/LocalTimeDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/MonthDayDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/YearDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/YearMonthDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/DurationKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/InstantKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/Jsr310KeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateTimeKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/LocalTimeKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/MonthDayKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/OffsetDateTimeKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/OffsetTimeKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/PeriodKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/YearKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/YearMonthKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/ZoneIdKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/ZoneOffsetKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/DurationSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/InstantSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/InstantSerializerBase.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/JSR310FormattedSerializerBase.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/JSR310SerializerBase.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/JavaTimeSerializerModifier.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/LocalDateSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/LocalTimeSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/MonthDaySerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/OffsetTimeSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/YearMonthSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/YearSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/ZoneIdSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/ser/key/ZonedDateTimeKeySerializer.java create mode 100644 src/main/java/tools/jackson/databind/datetime/util/DecimalUtils.java create mode 100644 src/main/java/tools/jackson/databind/datetime/util/DurationUnitConverter.java diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java new file mode 100644 index 0000000000..66f699baf5 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java @@ -0,0 +1,77 @@ +package tools.jackson.databind.datetime; + +import tools.jackson.core.util.JacksonFeature; + +/** + * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeModule}). + * + * @since 2.16 + */ +public enum JavaTimeFeature implements JacksonFeature +{ + /** + * Feature that determines whether {@link java.time.ZoneId} is normalized + * (via call to {@code java.time.ZoneId#normalized()}) when deserializing + * types like {@link java.time.ZonedDateTime}. + *

+ * Default setting is enabled, for backwards-compatibility with + * Jackson 2.15. + */ + NORMALIZE_DESERIALIZED_ZONE_ID(true), + + /** + * Feature that determines whether the {@link java.util.TimeZone} of the + * {@link tools.jackson.databind.DeserializationContext} is used + * when leniently deserializing {@link java.time.LocalDate} or + * {@link java.time.LocalDateTime} from the UTC/ISO instant format. + *

+ * Default setting is disabled, for backwards-compatibility with + * Jackson 2.18. + * + * @since 2.19 + */ + USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING(false), + + /** + * Feature that controls whether stringified numbers (Strings that without + * quotes would be legal JSON Numbers) may be interpreted as + * timestamps (enabled) or not (disabled), in case where there is an + * explicitly defined pattern ({@code DateTimeFormatter}) for value. + *

+ * Note that when the default pattern is used (no custom pattern defined), + * stringified numbers are always accepted as timestamps regardless of + * this feature. + */ + ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS(false), + + /** + * Feature that determines whether {@link java.time.Month} is serialized + * and deserialized as using a zero-based index (FALSE) or a one-based index (TRUE). + * For example, "1" would be serialized/deserialized as Month.JANUARY if TRUE and Month.FEBRUARY if FALSE. + *

+ * Default setting is false, meaning that Month is serialized/deserialized as a zero-based index. + */ + ONE_BASED_MONTHS(false) + ; + + /** + * Whether feature is enabled or disabled by default. + */ + private final boolean _defaultState; + + private final int _mask; + + JavaTimeFeature(boolean enabledByDefault) { + _defaultState = enabledByDefault; + _mask = (1 << ordinal()); + } + + @Override + public boolean enabledByDefault() { return _defaultState; } + + @Override + public boolean enabledIn(int flags) { return (flags & _mask) != 0; } + + @Override + public int getMask() { return _mask; } +} diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java new file mode 100644 index 0000000000..d9039e1899 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java @@ -0,0 +1,252 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime; + +import java.time.*; + +import tools.jackson.core.Version; +import tools.jackson.core.json.PackageVersion; +import tools.jackson.core.util.JacksonFeatureSet; +import tools.jackson.databind.*; +import tools.jackson.databind.deser.ValueInstantiator; +import tools.jackson.databind.deser.ValueInstantiators; +import tools.jackson.databind.deser.std.StdValueInstantiator; +import tools.jackson.databind.introspect.AnnotatedClass; +import tools.jackson.databind.introspect.AnnotatedClassResolver; +import tools.jackson.databind.introspect.AnnotatedMethod; +import tools.jackson.databind.module.SimpleDeserializers; +import tools.jackson.databind.module.SimpleKeyDeserializers; +import tools.jackson.databind.module.SimpleSerializers; +import tools.jackson.databind.ser.std.ToStringSerializer; + +import tools.jackson.databind.datetime.deser.*; +import tools.jackson.databind.datetime.deser.key.*; +import tools.jackson.databind.datetime.ser.*; +import tools.jackson.databind.datetime.ser.key.ZonedDateTimeKeySerializer; + +/** + * Class that registers capability of serializing {@code java.time} objects with the Jackson core. + * + *

+ * ObjectMapper mapper = new ObjectMapper();
+ * mapper.registerModule(new JavaTimeModule());
+ * 
+ *

+ * Most {@code java.time} types are serialized as numbers (integers or decimals as appropriate) if the + * {@link tools.jackson.databind.SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} feature is enabled + * (or, for {@link Duration}, {@link tools.jackson.databind.SerializationFeature#WRITE_DURATIONS_AS_TIMESTAMPS}), + * and otherwise are serialized in standard + * ISO-8601 string representation. + * ISO-8601 specifies formats for representing offset dates and times, zoned dates and times, + * local dates and times, periods, durations, zones, and more. All {@code java.time} types + * have built-in translation to and from ISO-8601 formats. + *

+ * Granularity of timestamps is controlled through the companion features + * {@link tools.jackson.databind.SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} and + * {@link tools.jackson.databind.DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS}. For serialization, timestamps are + * written as fractional numbers (decimals), where the number is seconds and the decimal is fractional seconds, if + * {@code WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), with resolution as fine as nanoseconds depending on the + * underlying JDK implementation. If {@code WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} is disabled, timestamps are written as a whole number of + * milliseconds. At deserialization time, decimal numbers are always read as fractional second timestamps with up-to-nanosecond resolution, + * since the meaning of the decimal is unambiguous. The more ambiguous integer types are read as fractional seconds without a decimal point + * if {@code READ_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), and otherwise they are read as milliseconds. + *

+ * Some exceptions to this standard serialization/deserialization rule: + *

+ * + * @author Nick Williams + * @author Zoltan Kiss + * + * @since 2.6 + */ +public final class JavaTimeModule + extends JacksonModule + implements java.io.Serializable +{ + private static final long serialVersionUID = 1L; + + private JacksonFeatureSet _features + = JacksonFeatureSet.fromDefaults(JavaTimeFeature.values()); + + @Override + public String getModuleName() { + return getClass().getName(); + } + + @Override + public Version version() { + return PackageVersion.VERSION; + } + + public JavaTimeModule() { } + + public JavaTimeModule enable(JavaTimeFeature f) { + _features = _features.with(f); + return this; + } + + public JavaTimeModule disable(JavaTimeFeature f) { + _features = _features.without(f); + return this; + } + + @Override + public void setupModule(SetupContext context) { + context.addDeserializers(new SimpleDeserializers() + // // Instant variants: + .addDeserializer(Instant.class, + InstantDeserializer.INSTANT.withFeatures(_features)) + .addDeserializer(OffsetDateTime.class, + InstantDeserializer.OFFSET_DATE_TIME.withFeatures(_features)) + .addDeserializer(ZonedDateTime.class, + InstantDeserializer.ZONED_DATE_TIME.withFeatures(_features)) + + // // Other deserializers + .addDeserializer(Duration.class, DurationDeserializer.INSTANCE) + .addDeserializer(LocalDateTime.class, + LocalDateTimeDeserializer.INSTANCE.withFeatures(_features)) + .addDeserializer(LocalDate.class, + LocalDateDeserializer.INSTANCE.withFeatures(_features)) + .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE) + .addDeserializer(MonthDay.class, MonthDayDeserializer.INSTANCE) + .addDeserializer(OffsetTime.class, OffsetTimeDeserializer.INSTANCE) + .addDeserializer(Period.class, JSR310StringParsableDeserializer.PERIOD) + .addDeserializer(Year.class, YearDeserializer.INSTANCE) + .addDeserializer(YearMonth.class, YearMonthDeserializer.INSTANCE) + .addDeserializer(ZoneId.class, JSR310StringParsableDeserializer.ZONE_ID) + .addDeserializer(ZoneOffset.class, JSR310StringParsableDeserializer.ZONE_OFFSET) + ); + + // then serializers: + context.addSerializers(new SimpleSerializers() + .addSerializer(Duration.class, DurationSerializer.INSTANCE) + .addSerializer(Instant.class, InstantSerializer.INSTANCE) + .addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE) + .addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE) + .addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE) + .addSerializer(MonthDay.class, MonthDaySerializer.INSTANCE) + .addSerializer(OffsetDateTime.class, OffsetDateTimeSerializer.INSTANCE) + .addSerializer(OffsetTime.class, OffsetTimeSerializer.INSTANCE) + .addSerializer(Period.class, new ToStringSerializer(Period.class)) + .addSerializer(Year.class, YearSerializer.INSTANCE) + .addSerializer(YearMonth.class, YearMonthSerializer.INSTANCE) + + .addSerializer(ZonedDateTime.class, ZonedDateTimeSerializer.INSTANCE) + + // Need to override Type Id handling + // (actual concrete type is `ZoneRegion`, but that's not visible) + .addSerializer(ZoneId.class, new ZoneIdSerializer()) + + .addSerializer(ZoneOffset.class, new ToStringSerializer(ZoneOffset.class)) + ); + + // key serializers + context.addKeySerializers(new SimpleSerializers() + .addSerializer(ZonedDateTime.class, ZonedDateTimeKeySerializer.INSTANCE) + ); + + // key deserializers + context.addKeyDeserializers(new SimpleKeyDeserializers() + .addDeserializer(Duration.class, DurationKeyDeserializer.INSTANCE) + .addDeserializer(Instant.class, InstantKeyDeserializer.INSTANCE) + .addDeserializer(LocalDateTime.class, LocalDateTimeKeyDeserializer.INSTANCE) + .addDeserializer(LocalDate.class, LocalDateKeyDeserializer.INSTANCE) + .addDeserializer(LocalTime.class, LocalTimeKeyDeserializer.INSTANCE) + .addDeserializer(MonthDay.class, MonthDayKeyDeserializer.INSTANCE) + .addDeserializer(OffsetDateTime.class, OffsetDateTimeKeyDeserializer.INSTANCE) + .addDeserializer(OffsetTime.class, OffsetTimeKeyDeserializer.INSTANCE) + .addDeserializer(Period.class, PeriodKeyDeserializer.INSTANCE) + .addDeserializer(Year.class, YearKeyDeserializer.INSTANCE) + .addDeserializer(YearMonth.class, YearMonthKeyDeserializer.INSTANCE) + .addDeserializer(ZonedDateTime.class, ZonedDateTimeKeyDeserializer.INSTANCE) + .addDeserializer(ZoneId.class, ZoneIdKeyDeserializer.INSTANCE) + .addDeserializer(ZoneOffset.class, ZoneOffsetKeyDeserializer.INSTANCE) + ); + + // [modules-java8#274]: 1-based Month (de)serializer need to be applied via modifiers: + final boolean oneBasedMonthEnabled = _features.isEnabled(JavaTimeFeature.ONE_BASED_MONTHS); + context.addDeserializerModifier(new JavaTimeDeserializerModifier(oneBasedMonthEnabled)); + context.addSerializerModifier(new JavaTimeSerializerModifier(oneBasedMonthEnabled)); + + context.addValueInstantiators(new ValueInstantiators.Base() { + @Override + public ValueInstantiator modifyValueInstantiator(DeserializationConfig config, + BeanDescription beanDesc, ValueInstantiator defaultInstantiator) + { + JavaType type = beanDesc.getType(); + Class raw = type.getRawClass(); + + // 15-May-2015, tatu: In theory not safe, but in practice we do need to do "fuzzy" matching + // because we will (for now) be getting a subtype, but in future may want to downgrade + // to the common base type. Even more, serializer may purposefully force use of base type. + // So... in practice it really should always work, in the end. :) + if (ZoneId.class.isAssignableFrom(raw)) { + // let's assume we should be getting "empty" StdValueInstantiator here: + if (defaultInstantiator instanceof StdValueInstantiator) { + StdValueInstantiator inst = (StdValueInstantiator) defaultInstantiator; + // one further complication: we need ZoneId info, not sub-class + AnnotatedClass ac; + if (raw == ZoneId.class) { + ac = beanDesc.getClassInfo(); + } else { + // we don't need Annotations, so constructing directly is fine here + // even if it's not generally recommended + ac = AnnotatedClassResolver.resolve(config, + config.constructType(ZoneId.class), config); + } + if (!inst.canCreateFromString()) { + AnnotatedMethod factory = _findFactory(ac, "of", String.class); + if (factory != null) { + inst.configureFromStringCreator(factory); + } + // otherwise... should we indicate an error? + } + // return ZoneIdInstantiator.construct(config, beanDesc, defaultInstantiator); + } + } + return defaultInstantiator; + } + }); + } + + protected AnnotatedMethod _findFactory(AnnotatedClass cls, String name, Class... argTypes) + { + final int argCount = argTypes.length; + for (AnnotatedMethod method : cls.getFactoryMethods()) { + if (!name.equals(method.getName()) + || (method.getParameterCount() != argCount)) { + continue; + } + for (int i = 0; i < argCount; ++i) { + Class argType = method.getParameter(i).getRawType(); + if (!argType.isAssignableFrom(argTypes[i])) { + continue; + } + } + return method; + } + return null; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java new file mode 100644 index 0000000000..3b27463d3c --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java @@ -0,0 +1,214 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.math.BigDecimal; +import java.time.DateTimeException; +import java.time.Duration; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.JsonTokenId; +import tools.jackson.core.StreamReadCapability; +import tools.jackson.core.io.NumberInput; +import tools.jackson.databind.*; +import tools.jackson.databind.datetime.util.DecimalUtils; +import tools.jackson.databind.datetime.util.DurationUnitConverter; + +/** + * Deserializer for Java 8 temporal {@link Duration}s. + */ +public class DurationDeserializer extends JSR310DeserializerBase +{ + public static final DurationDeserializer INSTANCE = new DurationDeserializer(); + + /** + * When defined (not {@code null}) integer values will be converted into duration + * unit configured for the converter. + * Using this converter will typically override the value specified in + * {@link DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS} as it is + * considered that the unit set in {@link JsonFormat#pattern()} has precedence + * since it is more specific. + *

+ * See [jackson-modules-java8#184] for more info. + */ + protected final DurationUnitConverter _durationUnitConverter; + + /** + * Flag for JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + * + * @since 2.16 + */ + protected final Boolean _readTimestampsAsNanosOverride; + + public DurationDeserializer() { + super(Duration.class); + _durationUnitConverter = null; + _readTimestampsAsNanosOverride = null; + } + + /** + * @since 2.11 + */ + protected DurationDeserializer(DurationDeserializer base, Boolean leniency) { + super(base, leniency); + _durationUnitConverter = base._durationUnitConverter; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + } + + /** + * @since 2.12 + */ + protected DurationDeserializer(DurationDeserializer base, DurationUnitConverter converter) { + super(base, base._isLenient); + _durationUnitConverter = converter; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + } + + /** + * @since 2.16 + */ + protected DurationDeserializer(DurationDeserializer base, + Boolean leniency, + DurationUnitConverter converter, + Boolean readTimestampsAsNanosOverride) { + super(base, leniency); + _durationUnitConverter = converter; + _readTimestampsAsNanosOverride = readTimestampsAsNanosOverride; + } + + @Override + protected DurationDeserializer withLeniency(Boolean leniency) { + return new DurationDeserializer(this, leniency); + } + + protected DurationDeserializer withConverter(DurationUnitConverter converter) { + return new DurationDeserializer(this, converter); + } + + @Override + public ValueDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + { + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); + DurationDeserializer deser = this; + boolean leniency = _isLenient; + DurationUnitConverter unitConverter = _durationUnitConverter; + Boolean timestampsAsNanosOverride = _readTimestampsAsNanosOverride; + if (format != null) { + if (format.hasLenient()) { + leniency = format.getLenient(); + } + if (format.hasPattern()) { + final String pattern = format.getPattern(); + unitConverter = DurationUnitConverter.from(pattern); + if (unitConverter == null) { + ctxt.reportBadDefinition(getValueType(ctxt), + String.format( + "Bad 'pattern' definition (\"%s\") for `Duration`: expected one of [%s]", + pattern, DurationUnitConverter.descForAllowed())); + } + } + timestampsAsNanosOverride = + format.getFeature(JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + if (leniency != _isLenient + || !Objects.equals(unitConverter, _durationUnitConverter) + || !Objects.equals(timestampsAsNanosOverride, _readTimestampsAsNanosOverride)) { + return new DurationDeserializer( + this, leniency, unitConverter, timestampsAsNanosOverride); + } + return deser; + } + + @Override + public Duration deserialize(JsonParser parser, DeserializationContext context) + throws JacksonException + { + switch (parser.currentTokenId()) + { + case JsonTokenId.ID_NUMBER_FLOAT: + BigDecimal value = parser.getDecimalValue(); + // [modules-java8#337] since 2.19, Duration does not need negative adjustment + return DecimalUtils.extractSecondsAndNanos(value, Duration::ofSeconds, false); + case JsonTokenId.ID_NUMBER_INT: + return _fromTimestamp(context, parser.getLongValue()); + case JsonTokenId.ID_STRING: + return _fromString(parser, context, parser.getString()); + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + case JsonTokenId.ID_START_OBJECT: + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + case JsonTokenId.ID_EMBEDDED_OBJECT: + // 20-Apr-2016, tatu: Related to [databind#1208], can try supporting embedded + // values quite easily + return (Duration) parser.getEmbeddedObject(); + + case JsonTokenId.ID_START_ARRAY: + return _deserializeFromArray(parser, context); + } + return _handleUnexpectedToken(context, parser, JsonToken.VALUE_STRING, + JsonToken.VALUE_NUMBER_INT, JsonToken.VALUE_NUMBER_FLOAT); + } + + protected Duration _fromString(JsonParser parser, DeserializationContext ctxt, + String value0) + throws JacksonException + { + String value = value0.trim(); + if (value.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(parser, ctxt, value); + } + // 30-Sep-2020: Should allow use of "Timestamp as String" for + // some textual formats + if (ctxt.isEnabled(StreamReadCapability.UNTYPED_SCALARS) + && _isValidTimestampString(value)) { + return _fromTimestamp(ctxt, NumberInput.parseLong(value)); + } + + try { + return Duration.parse(value); + } catch (DateTimeException e) { + // null format -> "default formatter" + return _handleDateTimeFormatException(ctxt, e, null, value); + } + } + + protected Duration _fromTimestamp(DeserializationContext ctxt, long ts) + { + if (_durationUnitConverter != null) { + return _durationUnitConverter.convert(ts); + } + // 20-Oct-2020, tatu: This makes absolutely no sense but... somehow + // became the default handling. + if (shouldReadTimestampsAsNanoseconds(ctxt)) { + return Duration.ofSeconds(ts); + } + return Duration.ofMillis(ts); + } + + protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) { + return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride : + context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java new file mode 100644 index 0000000000..bd5659cc29 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java @@ -0,0 +1,565 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.math.BigDecimal; +import java.time.*; +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.Matcher; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.*; +import tools.jackson.core.io.NumberInput; +import tools.jackson.core.util.JacksonFeatureSet; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.util.DecimalUtils; + +/** + * Deserializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime}, + * and {@link ZonedDateTime}s. + * + * @author Nick Williams + */ +public class InstantDeserializer + extends JSR310DateTimeDeserializerBase +{ + private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault(); + private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS + = JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault(); + + /** + * Constants used to check if ISO 8601 time string is colon-less. See [jackson-modules-java8#131] + */ + protected static final Pattern ISO8601_COLONLESS_OFFSET_REGEX = Pattern.compile("[+-][0-9]{4}(?=\\[|$)"); + + // @since 2.18.2 + private static OffsetDateTime decimalToOffsetDateTime(FromDecimalArguments args) { + // [jackson-modules-java8#308] Since 2.18.2 : Fix can't deserialize OffsetDateTime.MIN: Invalid value for EpochDay + if (args.integer == OffsetDateTime.MIN.toEpochSecond() && args.fraction == OffsetDateTime.MIN.getNano()) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(OffsetDateTime.MIN.toEpochSecond(), OffsetDateTime.MIN.getNano()), OffsetDateTime.MIN.getOffset()); + } + // [jackson-modules-java8#308] Since 2.18.2 : For OffsetDateTime.MAX case + if (args.integer == OffsetDateTime.MAX.toEpochSecond() && args.fraction == OffsetDateTime.MAX.getNano()) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(OffsetDateTime.MAX.toEpochSecond(), OffsetDateTime.MAX.getNano()), OffsetDateTime.MAX.getOffset()); + } + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(args.integer, args.fraction), args.zoneId); + } + + public static final InstantDeserializer INSTANT = new InstantDeserializer<>( + Instant.class, DateTimeFormatter.ISO_INSTANT, + Instant::from, + a -> Instant.ofEpochMilli(a.value), + a -> Instant.ofEpochSecond(a.integer, a.fraction), + null, + true, // yes, replace zero offset with Z + DEFAULT_NORMALIZE_ZONE_ID, + DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS + ); + + public static final InstantDeserializer OFFSET_DATE_TIME = new InstantDeserializer<>( + OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME, + OffsetDateTime::from, + a -> OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId), + InstantDeserializer::decimalToOffsetDateTime, + (d, z) -> (d.isEqual(OffsetDateTime.MIN) || d.isEqual(OffsetDateTime.MAX) ? d : d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime()))), + true, // yes, replace zero offset with Z + DEFAULT_NORMALIZE_ZONE_ID, + DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS + ); + + public static final InstantDeserializer ZONED_DATE_TIME = new InstantDeserializer<>( + ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME, + ZonedDateTime::from, + a -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId), + a -> ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId), + ZonedDateTime::withZoneSameInstant, + false, // keep zero offset and Z separate since zones explicitly supported + DEFAULT_NORMALIZE_ZONE_ID, + DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS + ); + + protected final Function fromMilliseconds; + + protected final Function fromNanoseconds; + + protected final Function parsedToValue; + + protected final BiFunction adjust; + + /** + * In case of vanilla `Instant` we seem to need to translate "+0000 | +00:00 | +00" + * timezone designator into plain "Z" for some reason; see + * [jackson-modules-java8#18] for more info + * + * @since 2.9.0 + */ + protected final boolean replaceZeroOffsetAsZ; + + /** + * Flag for JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE + */ + protected final Boolean _adjustToContextTZOverride; + + /** + * Flag for JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + * + * @since 2.16 + */ + protected final Boolean _readTimestampsAsNanosOverride; + + /** + * Flag set from + * {@link tools.jackson.databind.datetime.JavaTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to + * determine whether {@link ZoneId} is to be normalized during deserialization. + * + * @since 2.16 + */ + protected final boolean _normalizeZoneId; + + /** + * Flag set from + * {@link tools.jackson.databind.datetime.JavaTimeFeature#ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS} + * to determine whether stringified numbers are interpreted as timestamps + * (enabled) nor not (disabled) in addition to a custom pattern ({code DateTimeFormatter}). + *

+ * NOTE: stringified timestamps are always allowed with default patterns; + * this flag only affects handling of custom patterns. + * + * @since 2.16 + */ + protected final boolean _alwaysAllowStringifiedDateTimestamps; + + /** + * @since 2.16 + */ + protected InstantDeserializer(Class supportedType, + DateTimeFormatter formatter, + Function parsedToValue, + Function fromMilliseconds, + Function fromNanoseconds, + BiFunction adjust, + boolean replaceZeroOffsetAsZ, + boolean normalizeZoneId, + boolean readNumericStringsAsTimestamp + ) + { + super(supportedType, formatter); + this.parsedToValue = parsedToValue; + this.fromMilliseconds = fromMilliseconds; + this.fromNanoseconds = fromNanoseconds; + this.adjust = adjust == null ? ((d, z) -> d) : adjust; + this.replaceZeroOffsetAsZ = replaceZeroOffsetAsZ; + this._adjustToContextTZOverride = null; + this._readTimestampsAsNanosOverride = null; + _normalizeZoneId = normalizeZoneId; + _alwaysAllowStringifiedDateTimestamps = readNumericStringsAsTimestamp; + } + + @SuppressWarnings("unchecked") + protected InstantDeserializer(InstantDeserializer base, DateTimeFormatter f) + { + super((Class) base.handledType(), f); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT); + _adjustToContextTZOverride = base._adjustToContextTZOverride; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + _normalizeZoneId = base._normalizeZoneId; + _alwaysAllowStringifiedDateTimestamps = base._alwaysAllowStringifiedDateTimestamps; + } + + @SuppressWarnings("unchecked") + protected InstantDeserializer(InstantDeserializer base, Boolean adjustToContextTimezoneOverride) + { + super((Class) base.handledType(), base._formatter); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ; + _adjustToContextTZOverride = adjustToContextTimezoneOverride; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + _normalizeZoneId = base._normalizeZoneId; + _alwaysAllowStringifiedDateTimestamps = base._alwaysAllowStringifiedDateTimestamps; + } + + @SuppressWarnings("unchecked") + protected InstantDeserializer(InstantDeserializer base, DateTimeFormatter f, Boolean leniency) + { + super((Class) base.handledType(), f, leniency); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT); + _adjustToContextTZOverride = base._adjustToContextTZOverride; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + _normalizeZoneId = base._normalizeZoneId; + _alwaysAllowStringifiedDateTimestamps = base._alwaysAllowStringifiedDateTimestamps; + } + + /** + * @since 2.16 + */ + protected InstantDeserializer(InstantDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape, + Boolean adjustToContextTimezoneOverride, + Boolean readTimestampsAsNanosOverride) + { + super(base, leniency, formatter, shape); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ; + _adjustToContextTZOverride = adjustToContextTimezoneOverride; + _readTimestampsAsNanosOverride = readTimestampsAsNanosOverride; + _normalizeZoneId = base._normalizeZoneId; + _alwaysAllowStringifiedDateTimestamps = base._alwaysAllowStringifiedDateTimestamps; + } + + /** + * @since 2.16 + */ + @SuppressWarnings("unchecked") + protected InstantDeserializer(InstantDeserializer base, + JacksonFeatureSet features) + { + super((Class) base.handledType(), base._formatter); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + replaceZeroOffsetAsZ = base.replaceZeroOffsetAsZ; + _adjustToContextTZOverride = base._adjustToContextTZOverride; + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + + _normalizeZoneId = features.isEnabled(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID); + _alwaysAllowStringifiedDateTimestamps = features.isEnabled(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS); + } + + @Override + protected InstantDeserializer withDateFormat(DateTimeFormatter dtf) { + if (dtf == _formatter) { + return this; + } + return new InstantDeserializer<>(this, dtf); + } + + @Override + protected InstantDeserializer withLeniency(Boolean leniency) { + return new InstantDeserializer<>(this, _formatter, leniency); + } + + // @since 2.16 + public InstantDeserializer withFeatures(JacksonFeatureSet features) { + if ((_normalizeZoneId == features.isEnabled(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) + && (_alwaysAllowStringifiedDateTimestamps == features.isEnabled(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + ) { + return this; + } + return new InstantDeserializer<>(this, features); + } + + @SuppressWarnings("unchecked") + @Override // @since 2.12.1 + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) + { + InstantDeserializer deser = (InstantDeserializer) super._withFormatOverrides(ctxt, + property, formatOverrides); + Boolean adjustToContextTZOverride = formatOverrides.getFeature( + JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature( + JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + if (!Objects.equals(adjustToContextTZOverride, deser._adjustToContextTZOverride) + || !Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) { + return new InstantDeserializer<>(deser, deser._isLenient, deser._formatter, + deser._shape, adjustToContextTZOverride, readTimestampsAsNanosOverride); + } + return deser; + } + + @SuppressWarnings("unchecked") + @Override + public T deserialize(JsonParser parser, DeserializationContext context) + throws JacksonException + { + //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only + //string values have to be adjusted to the configured TZ. + switch (parser.currentTokenId()) + { + case JsonTokenId.ID_NUMBER_FLOAT: + return _fromDecimal(context, parser.getDecimalValue()); + case JsonTokenId.ID_NUMBER_INT: + return _fromLong(context, parser.getLongValue()); + case JsonTokenId.ID_STRING: + return _fromString(parser, context, parser.getString()); + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + case JsonTokenId.ID_START_OBJECT: + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + case JsonTokenId.ID_EMBEDDED_OBJECT: + // 20-Apr-2016, tatu: Related to [databind#1208], can try supporting embedded + // values quite easily + return (T) parser.getEmbeddedObject(); + + case JsonTokenId.ID_START_ARRAY: + return _deserializeFromArray(parser, context); + } + return _handleUnexpectedToken(context, parser, JsonToken.VALUE_STRING, + JsonToken.VALUE_NUMBER_INT, JsonToken.VALUE_NUMBER_FLOAT); + } + + protected boolean shouldAdjustToContextTimezone(DeserializationContext context) { + return (_adjustToContextTZOverride != null) ? _adjustToContextTZOverride : + context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + } + + protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) { + return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride : + context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + // Helper method to find Strings of form "all digits" and "digits-comma-digits" + protected int _countPeriods(String str) + { + int commas = 0; + int i = 0; + int ch = str.charAt(i); + if (ch == '-' || ch == '+') { + ++i; + } + for (int end = str.length(); i < end; ++i) { + ch = str.charAt(i); + if (ch < '0' || ch > '9') { + if (ch == '.') { + ++commas; + } else { + return -1; + } + } + } + return commas; + } + + protected T _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + // only check for other parsing modes if we are using default formatter or explicitly asked to + if (_alwaysAllowStringifiedDateTimestamps || + _formatter == DateTimeFormatter.ISO_INSTANT || + _formatter == DateTimeFormatter.ISO_OFFSET_DATE_TIME || + _formatter == DateTimeFormatter.ISO_ZONED_DATE_TIME + ) { + // 22-Jan-2016, [datatype-jsr310#16]: Allow quoted numbers too + int dots = _countPeriods(string); + if (dots >= 0) { // negative if not simple number + try { + if (dots == 0) { + return _fromLong(ctxt, NumberInput.parseLong(string)); + } + if (dots == 1) { + return _fromDecimal(ctxt, NumberInput.parseBigDecimal(string, false)); + } + } catch (NumberFormatException e) { + // fall through to default handling, to get error there + } + } + + string = replaceZeroOffsetAsZIfNecessary(string); + } + + // For some reason DateTimeFormatter.ISO_INSTANT only supports UTC ISO 8601 strings, so it have to be excluded + if (_formatter == DateTimeFormatter.ISO_OFFSET_DATE_TIME || + _formatter == DateTimeFormatter.ISO_ZONED_DATE_TIME) { + + // 21-March-2021, Oeystein: Work-around to support basic iso 8601 format (colon-less). + // As per JSR-310; Only extended 8601 formats (with colon) are supported for + // ZonedDateTime.parse() and OffsetDateTime.parse(). + // https://github.com/FasterXML/jackson-modules-java8/issues/131 + string = addInColonToOffsetIfMissing(string); + } + + T value; + try { + TemporalAccessor acc = _formatter.parse(string); + value = parsedToValue.apply(acc); + if (shouldAdjustToContextTimezone(ctxt)) { + return adjust.apply(value, getZone(ctxt)); + } + } catch (DateTimeException e) { + value = _handleDateTimeFormatException(ctxt, e, _formatter, string); + } + return value; + } + + protected T _fromLong(DeserializationContext context, long timestamp) + { + if(shouldReadTimestampsAsNanoseconds(context)){ + return fromNanoseconds.apply(new FromDecimalArguments( + timestamp, 0, this.getZone(context) + )); + } + return fromMilliseconds.apply(new FromIntegerArguments( + timestamp, this.getZone(context))); + } + + protected T _fromDecimal(DeserializationContext context, BigDecimal value) + { + FromDecimalArguments args = + DecimalUtils.extractSecondsAndNanos(value, (s, ns) -> new FromDecimalArguments(s, ns, getZone(context)), + // [modules-java8#337] since 2.19, only Instant needs negative adjustment + true); + return fromNanoseconds.apply(args); + } + + private ZoneId getZone(DeserializationContext context) + { + // Instants are always in UTC, so don't waste compute cycles + // Normalizing the zone to prevent discrepancies. + // See https://github.com/FasterXML/jackson-modules-java8/pull/267 for details + if (_valueClass == Instant.class) { + return null; + } + ZoneId zoneId = context.getTimeZone().toZoneId(); + return _normalizeZoneId ? zoneId.normalized() : zoneId; + } + + private String replaceZeroOffsetAsZIfNecessary(String text) + { + if (replaceZeroOffsetAsZ) { + return replaceZeroOffsetAsZ(text); + } + + return text; + } + + private static String replaceZeroOffsetAsZ(String text) + { + int plusIndex = text.lastIndexOf('+'); + if (plusIndex < 0) { + return text; + } + int maybeOffsetIndex = plusIndex + 1; + int remaining = text.length() - maybeOffsetIndex; + switch (remaining) { + case 2: + return text.regionMatches(maybeOffsetIndex, "00", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + case 4: + return text.regionMatches(maybeOffsetIndex, "0000", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + case 5: + return text.regionMatches(maybeOffsetIndex, "00:00", 0, remaining) + ? text.substring(0, plusIndex) + 'Z' + : text; + } + return text; + } + + // @since 2.13 + private static String addInColonToOffsetIfMissing(String text) + { + int timeIndex = text.indexOf('T'); + if (timeIndex < 0 || timeIndex > text.length() - 1) { + return text; + } + + int offsetIndex = text.indexOf('+', timeIndex + 1); + if (offsetIndex < 0) { + offsetIndex = text.indexOf('-', timeIndex + 1); + } + + if (offsetIndex < 0 || offsetIndex > text.length() - 5) { + return text; + } + + int colonIndex = text.indexOf(':', offsetIndex); + if (colonIndex == offsetIndex + 3) { + return text; + } + + if (Character.isDigit(text.charAt(offsetIndex + 1)) + && Character.isDigit(text.charAt(offsetIndex + 2)) + && Character.isDigit(text.charAt(offsetIndex + 3)) + && Character.isDigit(text.charAt(offsetIndex + 4))) { + String match = text.substring(offsetIndex, offsetIndex + 5); + return text.substring(0, offsetIndex) + + match.substring(0, 3) + ':' + match.substring(3) + + text.substring(offsetIndex + match.length()); + } + + // fallback to slow regex path, should be fully handled by the above + final Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher(text); + if (matcher.find()) { + String match = matcher.group(0); + return matcher.replaceFirst(match.substring(0, 3) + ':' + match.substring(3)); + } + return text; + } + + public static class FromIntegerArguments // since 2.8.3 + { + public final long value; + public final ZoneId zoneId; + + FromIntegerArguments(long value, ZoneId zoneId) + { + this.value = value; + this.zoneId = zoneId; + } + } + + public static class FromDecimalArguments // since 2.8.3 + { + public final long integer; + public final int fraction; + public final ZoneId zoneId; + + FromDecimalArguments(long integer, int fraction, ZoneId zoneId) + { + this.integer = integer; + this.fraction = fraction; + this.zoneId = zoneId; + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java b/src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java new file mode 100644 index 0000000000..e87ca927f1 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java @@ -0,0 +1,175 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.util.Locale; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; + +import tools.jackson.databind.*; + +public abstract class JSR310DateTimeDeserializerBase + extends JSR310DeserializerBase +{ + protected final DateTimeFormatter _formatter; + + /** + * Setting that indicates the {@Link JsonFormat.Shape} specified for this deserializer + * as a {@link com.fasterxml.jackson.annotation.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. + */ + protected final Shape _shape; + + protected JSR310DateTimeDeserializerBase(Class supportedType, DateTimeFormatter f) { + super(supportedType); + _formatter = f; + _shape = null; + } + + public JSR310DateTimeDeserializerBase(Class supportedType, DateTimeFormatter f, Boolean leniency) { + super(supportedType, leniency); + _formatter = f; + _shape = null; + } + + protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, + DateTimeFormatter f) { + super(base); + _formatter = f; + _shape = base._shape; + } + + protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, + Boolean leniency) { + super(base, leniency); + _formatter = base._formatter; + _shape = base._shape; + } + + protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, + Shape shape) { + super(base); + _formatter = base._formatter; + _shape = shape; + } + + /** + * @since 2.16 + */ + protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape) { + super(base, leniency); + _formatter = formatter; + _shape = shape; + } + + protected abstract JSR310DateTimeDeserializerBase withDateFormat(DateTimeFormatter dtf); + + @Override + protected abstract JSR310DateTimeDeserializerBase withLeniency(Boolean leniency); + + /** + * The default implementation returns this, because shape is more likely applicable in case of the serialization, + * usage during deserialization could cover only very specific cases. + */ + protected JSR310DateTimeDeserializerBase withShape(Shape shape) { + return this; + } + + @Override + public ValueDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + { + 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; + + // 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 (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(); + } + 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 (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); + if (enabled == null) { + enabled = ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES); + } + return enabled; + } + + protected void _throwNoNumericTimestampNeedTimeZone(JsonParser p, DeserializationContext ctxt) + throws JacksonException + { + ctxt.reportInputMismatch(handledType(), +"raw timestamp (%d) not allowed for `%s`: need additional information such as an offset or time-zone (see class Javadocs)", +p.getNumberValue(), handledType().getName()); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java b/src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java new file mode 100644 index 0000000000..93b50c9048 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java @@ -0,0 +1,240 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.io.NumberInput; + +import tools.jackson.databind.*; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.deser.std.StdScalarDeserializer; +import tools.jackson.databind.jsontype.TypeDeserializer; +import tools.jackson.databind.type.LogicalType; +import tools.jackson.databind.util.ClassUtil; + +/** + * Base class that indicates that all JSR310 datatypes are deserialized from scalar JSON types. + * + * @author Nick Williams + */ +abstract class JSR310DeserializerBase extends StdScalarDeserializer +{ + /** + * Flag that indicates what leniency setting is enabled for this deserializer (either + * due {@link com.fasterxml.jackson.annotation.JsonFormat.Shape} annotation on property or class, or due to per-type + * "config override", or from global settings): leniency/strictness has effect + * on accepting some non-default input value representations (such as integer values + * for dates). + *

+ * Note that global default setting is for leniency to be enabled, for Jackson 2.x, + * and has to be explicitly change to force strict handling: this is to keep backwards + * compatibility with earlier versions. + */ + protected final boolean _isLenient; + + protected JSR310DeserializerBase(Class supportedType) { + super(supportedType); + _isLenient = true; + } + + protected JSR310DeserializerBase(Class supportedType, + Boolean leniency) { + super(supportedType); + _isLenient = !Boolean.FALSE.equals(leniency); + } + + protected JSR310DeserializerBase(JSR310DeserializerBase base) { + super(base); + _isLenient = base._isLenient; + } + + protected JSR310DeserializerBase(JSR310DeserializerBase base, Boolean leniency) { + super(base); + _isLenient = !Boolean.FALSE.equals(leniency); + } + + protected abstract JSR310DeserializerBase withLeniency(Boolean leniency); + + /** + * @return {@code true} if lenient handling is enabled; {code false} if not (strict mode) + */ + protected boolean isLenient() { + return _isLenient; + } + + /** + * Replacement for {@code isLenient()} for specific case of deserialization + * from empty or blank String. + * + * @since 2.12 + */ + @SuppressWarnings("unchecked") + protected T _fromEmptyString(JsonParser p, DeserializationContext ctxt, + String str) + throws JacksonException + { + final CoercionAction act = _checkFromStringCoercion(ctxt, str); + switch (act) { // note: Fail handled above + case AsEmpty: + return (T) getEmptyValue(ctxt); + case TryConvert: + case AsNull: + default: + } + // 22-Oct-2020, tatu: Although we should probably just accept this, + // for backwards compatibility let's for now allow override by + // "Strict" checks + if (!_isLenient) { + return _failForNotLenient(p, ctxt, JsonToken.VALUE_STRING); + } + + return null; + } + + // Presumably all types here are Date/Time oriented ones? + @Override + public LogicalType logicalType() { return LogicalType.DateTime; } + + @Override + public Object deserializeWithType(JsonParser parser, DeserializationContext context, + TypeDeserializer typeDeserializer) + throws JacksonException + { + return typeDeserializer.deserializeTypedFromAny(parser, context); + } + + // @since 2.12 + protected boolean _isValidTimestampString(String str) { + // 30-Sep-2020, tatu: Need to support "numbers as Strings" for data formats + // that only have String values for scalars (CSV, Properties, XML) + // NOTE: we do allow negative values, but has to fit in 64-bits: + return _isIntNumber(str) && NumberInput.inLongRange(str, (str.charAt(0) == '-')); + } + + protected BOGUS _reportWrongToken(DeserializationContext context, + JsonToken exp, String unit) + throws JacksonException + { + context.reportWrongTokenException((ValueDeserializer)this, exp, + "Expected %s for '%s' of %s value", + exp.name(), unit, ClassUtil.getClassDescription(handledType())); + return null; + } + + protected BOGUS _reportWrongToken(JsonParser parser, DeserializationContext context, + JsonToken... expTypes) + throws JacksonException + { + // 20-Apr-2016, tatu: No multiple-expected-types handler yet, construct message here + return context.reportInputMismatch(handledType(), + "Unexpected token (%s), expected one of %s for %s value", + parser.currentToken(), + Arrays.asList(expTypes).toString(), + ClassUtil.getClassDescription(handledType())); + } + + @SuppressWarnings("unchecked") + protected R _handleDateTimeException(DeserializationContext context, + DateTimeException e0, String value) + throws JacksonException + { + try { + return (R) context.handleWeirdStringValue(handledType(), value, + "Failed to deserialize %s: (%s) %s", + ClassUtil.getClassDescription(handledType()), e0.getClass().getName(), e0.getMessage()); + } catch (JacksonException e) { + e.initCause(e0); + throw e; + } + } + + // @since 3.0 + @SuppressWarnings("unchecked") + protected R _handleDateTimeFormatException(DeserializationContext context, + DateTimeException e0, DateTimeFormatter format, String value) + throws JacksonException + { + final String formatterDesc = (format == null) ? "[default format]" : format.toString(); + try { + return (R) context.handleWeirdStringValue(handledType(), value, + "Failed to deserialize %s (with format '%s'): (%s) %s", + ClassUtil.getClassDescription(handledType()), + formatterDesc, e0.getClass().getName(), e0.getMessage()); + } catch (JacksonException e) { + e.initCause(e0); + throw e; + } + } + + @SuppressWarnings("unchecked") + protected R _handleUnexpectedToken(DeserializationContext ctxt, + JsonParser parser, String message, Object... args) + { + return (R) ctxt.handleUnexpectedToken(getValueType(ctxt), parser.currentToken(), + parser, message, args); + } + + protected R _handleUnexpectedToken(DeserializationContext context, + JsonParser parser, JsonToken... expTypes) + { + return _handleUnexpectedToken(context, parser, + "Unexpected token (%s), expected one of %s for %s value", + parser.currentToken(), + Arrays.asList(expTypes), + ClassUtil.getClassDescription(handledType())); + } + + @SuppressWarnings("unchecked") + protected T _failForNotLenient(JsonParser p, DeserializationContext ctxt, + JsonToken expToken) + throws JacksonException + { + return (T) ctxt.handleUnexpectedToken(getValueType(ctxt), expToken, p, + "Cannot deserialize instance of %s out of %s token: not allowed because 'strict' mode set for property or type (enable 'lenient' handling to allow)", + ClassUtil.nameOf(handledType()), p.currentToken()); + } + + /* + public Object handleUnexpectedToken(Class instClass, JsonToken t, + JsonParser p, String msg, Object... msgArgs) + */ + + /** + * Helper method used to peel off spurious wrappings of DateTimeException + * + * @param e DateTimeException to peel + * + * @return DateTimeException that does not have another DateTimeException as its cause. + */ + protected DateTimeException _peelDTE(DateTimeException e) { + while (true) { + Throwable t = e.getCause(); + if (t != null && t instanceof DateTimeException) { + e = (DateTimeException) t; + continue; + } + break; + } + return e; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java new file mode 100644 index 0000000000..93161fac17 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java @@ -0,0 +1,186 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; + +import tools.jackson.core.JsonToken; + +import tools.jackson.core.util.VersionUtil; + +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.jsontype.TypeDeserializer; + +/** + * Deserializer for all Java 8 temporal {@link java.time} types that cannot be represented + * with numbers and that have parse functions that can take {@link String}s, + * and where format is not configurable. + * + * @author Nick Williams + * @author Tatu Saloranta + */ +public class JSR310StringParsableDeserializer + extends JSR310DeserializerBase +{ + protected final static int TYPE_PERIOD = 1; + protected final static int TYPE_ZONE_ID = 2; + protected final static int TYPE_ZONE_OFFSET = 3; + + public static final ValueDeserializer PERIOD = + createDeserializer(Period.class, TYPE_PERIOD); + + public static final ValueDeserializer ZONE_ID = + createDeserializer(ZoneId.class, TYPE_ZONE_ID); + + public static final ValueDeserializer ZONE_OFFSET = + createDeserializer(ZoneOffset.class, TYPE_ZONE_OFFSET); + + protected final int _typeSelector; + + @SuppressWarnings("unchecked") + protected JSR310StringParsableDeserializer(Class supportedType, int typeSelector) + { + super((Class)supportedType); + _typeSelector = typeSelector; + } + + protected JSR310StringParsableDeserializer(JSR310StringParsableDeserializer base, Boolean leniency) { + super(base, leniency); + _typeSelector = base._typeSelector; + } + + @SuppressWarnings("unchecked") + protected static ValueDeserializer createDeserializer(Class type, int typeId) { + return (ValueDeserializer) new JSR310StringParsableDeserializer(type, typeId); + } + + @Override + protected JSR310StringParsableDeserializer withLeniency(Boolean leniency) { + if (_isLenient == !Boolean.FALSE.equals(leniency)) { + return this; + } + // TODO: or should this be casting as above in createDeserializer? But then in createContext, we need to + // call the withLeniency method in this class. (See if we can follow InstantDeser convention here?) + return new JSR310StringParsableDeserializer(this, leniency); + } + + @Override + public ValueDeserializer createContextual(DeserializationContext ctxt, + BeanProperty property) + { + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); + JSR310StringParsableDeserializer deser = this; + if (format != null) { + if (format.hasLenient()) { + Boolean leniency = format.getLenient(); + if (leniency != null) { + deser = this.withLeniency(leniency); + } + } + } + return deser; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) + throws JacksonException + { + if (p.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(p, ctxt, p.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (p.isExpectedStartObjectToken()) { + return _fromString(p, ctxt, + ctxt.extractScalarFromObject(p, this, handledType())); + } + if (p.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + // 20-Apr-2016, tatu: Related to [databind#1208], can try supporting embedded + // values quite easily + return p.getEmbeddedObject(); + } + if (p.isExpectedStartArrayToken()) { + return _deserializeFromArray(p, ctxt); + } + + throw ctxt.wrongTokenException(p, handledType(), JsonToken.VALUE_STRING, null); + } + + @Override + public Object deserializeWithType(JsonParser p, DeserializationContext context, + TypeDeserializer deserializer) + throws JacksonException + { + // This is a nasty kludge right here, working around issues like + // [datatype-jsr310#24]. But should work better than not having the work-around. + JsonToken t = p.currentToken(); + if ((t != null) && t.isScalarValue()) { + return deserialize(p, context); + } + return deserializer.deserializeTypedFromAny(p, context); + } + + protected Object _fromString(JsonParser p, DeserializationContext ctxt, + String string) + throws JacksonException + { + string = string.trim(); + if (string.length() == 0) { + CoercionAction act = ctxt.findCoercionAction(logicalType(), _valueClass, + CoercionInputShape.EmptyString); + if (act == CoercionAction.Fail) { + ctxt.reportInputMismatch(this, +"Cannot coerce empty String (\"\") to %s (but could if enabling coercion using `CoercionConfig`)", +_coercedTypeDesc()); + } + // 21-Jun-2020, tatu: As of 2.12, leniency considered legacy setting, + // but still supported. + if (!isLenient()) { + return _failForNotLenient(p, ctxt, JsonToken.VALUE_STRING); + } + if (act == CoercionAction.AsEmpty) { + return getEmptyValue(ctxt); + } + // None of the types has specific null value + return null; + } + try { + switch (_typeSelector) { + case TYPE_PERIOD: + return Period.parse(string); + case TYPE_ZONE_ID: + return ZoneId.of(string); + case TYPE_ZONE_OFFSET: + return ZoneOffset.of(string); + } + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, null, string); + } + VersionUtil.throwInternal(); + return null; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java b/src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java new file mode 100644 index 0000000000..f36d7c1802 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Month; + +import tools.jackson.databind.*; +import tools.jackson.databind.deser.ValueDeserializerModifier; + +/** + * @since 2.17 + */ +public class JavaTimeDeserializerModifier extends ValueDeserializerModifier { + private static final long serialVersionUID = 1L; + + private final boolean _oneBaseMonths; + + public JavaTimeDeserializerModifier(boolean oneBaseMonths) { + _oneBaseMonths = oneBaseMonths; + } + + @Override + public ValueDeserializer modifyEnumDeserializer(DeserializationConfig config, JavaType type, + BeanDescription beanDesc, ValueDeserializer defaultDeserializer) { + if (_oneBaseMonths && type.hasRawClass(Month.class)) { + return new OneBasedMonthDeserializer(defaultDeserializer); + } + return defaultDeserializer; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java new file mode 100644 index 0000000000..6e8851e586 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java @@ -0,0 +1,215 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.*; +import tools.jackson.core.util.JacksonFeatureSet; + +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.JavaType; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; + +import tools.jackson.databind.datetime.JavaTimeFeature; + +/** + * Deserializer for Java 8 temporal {@link LocalDate}s. + * + * @author Nick Williams + */ +public class LocalDateDeserializer extends JSR310DateTimeDeserializerBase +{ + private final static boolean DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING + = JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); + + private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; + + public static final LocalDateDeserializer INSTANCE = new LocalDateDeserializer(); + + /** + * Flag set from + * {@link tools.jackson.databind.datetime.JavaTimeFeature#USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING} + * to determine whether the {@link java.util.TimeZone} of the + * {@link tools.jackson.databind.DeserializationContext} is used + * when leniently deserializing from the UTC/ISO instant format. + */ + protected final boolean _useTimeZoneForLenientDateParsing; + + protected LocalDateDeserializer() { + this(DEFAULT_FORMATTER); + } + + public LocalDateDeserializer(DateTimeFormatter dtf) { + super(LocalDate.class, dtf); + _useTimeZoneForLenientDateParsing = DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING; + } + + public LocalDateDeserializer(LocalDateDeserializer base, DateTimeFormatter dtf) { + super(base, dtf); + _useTimeZoneForLenientDateParsing = base._useTimeZoneForLenientDateParsing; + } + + protected LocalDateDeserializer(LocalDateDeserializer base, Boolean leniency) { + super(base, leniency); + _useTimeZoneForLenientDateParsing = base._useTimeZoneForLenientDateParsing; + } + + protected LocalDateDeserializer(LocalDateDeserializer base, JsonFormat.Shape shape) { + super(base, shape); + _useTimeZoneForLenientDateParsing = base._useTimeZoneForLenientDateParsing; + } + + /** + * Since 2.19 + */ + protected LocalDateDeserializer(LocalDateDeserializer base, JacksonFeatureSet features) { + super(LocalDate.class, base._formatter); + _useTimeZoneForLenientDateParsing = features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); + } + + @Override + protected LocalDateDeserializer withDateFormat(DateTimeFormatter dtf) { + return new LocalDateDeserializer(this, dtf); + } + + @Override + protected LocalDateDeserializer withLeniency(Boolean leniency) { + return new LocalDateDeserializer(this, leniency); + } + + @Override + protected LocalDateDeserializer withShape(JsonFormat.Shape shape) { return new LocalDateDeserializer(this, shape); } + + /** + * Since 2.19 + */ + public LocalDateDeserializer withFeatures(JacksonFeatureSet features) { + if (_useTimeZoneForLenientDateParsing == + features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { + return this; + } + return new LocalDateDeserializer(this, features); + } + + @Override + public LocalDate deserialize(JsonParser parser, DeserializationContext context) + throws JacksonException + { + if (parser.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (parser.isExpectedStartArrayToken()) { + JsonToken t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if (context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + && (t == JsonToken.VALUE_STRING || t==JsonToken.VALUE_EMBEDDED_OBJECT)) { + final LocalDate parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + if (t == JsonToken.VALUE_NUMBER_INT) { + int year = parser.getIntValue(); + int month = parser.nextIntValue(-1); + int day = parser.nextIntValue(-1); + + if (parser.nextToken() != JsonToken.END_ARRAY) { + throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, + "Expected array to end"); + } + return LocalDate.of(year, month, day); + } + context.reportInputMismatch(handledType(), + "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT", + t); + } + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (LocalDate) parser.getEmbeddedObject(); + } + // 06-Jan-2018, tatu: Is this actually safe? Do users expect such coercion? + if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + CoercionAction act = context.findCoercionAction(logicalType(), _valueClass, + CoercionInputShape.Integer); + _checkCoercionFail(context, act, handledType(), parser.getLongValue(), + "Integer value (" + parser.getLongValue() + ")"); + + // 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 _failForNotLenient(parser, context, JsonToken.VALUE_STRING); + } + return _handleUnexpectedToken(context, parser, "Expected array or string."); + } + + protected LocalDate _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + // as per [datatype-jsr310#37], only check for optional (and, incorrect...) time marker 'T' + // if we are using default formatter + final DateTimeFormatter format = _formatter; + try { + 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 (isLenient()) { + if (string.endsWith("Z")) { + if (_useTimeZoneForLenientDateParsing) { + return Instant.parse(string).atZone(ctxt.getTimeZone().toZoneId()).toLocalDate(); + } + 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); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, format, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserializer.java new file mode 100644 index 0000000000..6f0c0661de --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserializer.java @@ -0,0 +1,254 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.*; +import tools.jackson.core.util.JacksonFeatureSet; +import tools.jackson.databind.*; +import tools.jackson.databind.datetime.JavaTimeFeature; + +/** + * Deserializer for Java 8 temporal {@link LocalDateTime}s. + * + * @author Nick Williams + */ +public class LocalDateTimeDeserializer + extends JSR310DateTimeDeserializerBase +{ + private final static boolean DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING + = JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); + + private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + + public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer(); + + /** + * Flag for JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + * + * @since 2.16 + */ + protected final Boolean _readTimestampsAsNanosOverride; + + /** + * Flag set from + * {@link tools.jackson.databind.datetime.JavaTimeFeature#USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING} + * to determine whether the {@link java.util.TimeZone} of the + * {@link tools.jackson.databind.DeserializationContext} is used + * when leniently deserializing from the UTC/ISO instant format. + * + * @since 2.19 + */ + protected final boolean _useTimeZoneForLenientDateParsing; + + protected LocalDateTimeDeserializer() { // was private before 2.12 + this(DEFAULT_FORMATTER); + } + + public LocalDateTimeDeserializer(DateTimeFormatter formatter) { + super(LocalDateTime.class, formatter); + _readTimestampsAsNanosOverride = null; + _useTimeZoneForLenientDateParsing = DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING; + } + + /** + * Since 2.10 + */ + protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, Boolean leniency) { + super(base, leniency); + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + _useTimeZoneForLenientDateParsing = base._useTimeZoneForLenientDateParsing; + } + + /** + * Since 2.16 + */ + protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape, + Boolean readTimestampsAsNanosOverride) { + super(base, leniency, formatter, shape); + _readTimestampsAsNanosOverride = readTimestampsAsNanosOverride; + _useTimeZoneForLenientDateParsing = base._useTimeZoneForLenientDateParsing; + } + + /** + * Since 2.19 + */ + protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, JacksonFeatureSet features) { + super(LocalDateTime.class, base._formatter); + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + _useTimeZoneForLenientDateParsing = features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); + } + + @Override + protected LocalDateTimeDeserializer withDateFormat(DateTimeFormatter dtf) { + return new LocalDateTimeDeserializer(this, _isLenient, dtf, _shape, _readTimestampsAsNanosOverride); + } + + @Override + protected LocalDateTimeDeserializer withLeniency(Boolean leniency) { + return new LocalDateTimeDeserializer(this, leniency); + } + + @Override + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) + { + LocalDateTimeDeserializer deser = (LocalDateTimeDeserializer) + super._withFormatOverrides(ctxt, property, formatOverrides); + Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature( + JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + if (!Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) { + return new LocalDateTimeDeserializer(deser, deser._isLenient, deser._formatter, + deser._shape, readTimestampsAsNanosOverride); + } + return deser; + } + + /** + * Since 2.19 + */ + public LocalDateTimeDeserializer withFeatures(JacksonFeatureSet features) { + if (_useTimeZoneForLenientDateParsing == + features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { + return this; + } + return new LocalDateTimeDeserializer(this, features); + } + + @Override + public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws JacksonException + { + if (parser.hasTokenId(JsonTokenId.ID_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (parser.isExpectedStartArrayToken()) { + JsonToken t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT) + && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + final LocalDateTime parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + if (t == JsonToken.VALUE_NUMBER_INT) { + LocalDateTime result; + + int year = parser.getIntValue(); + int month = parser.nextIntValue(-1); + int day = parser.nextIntValue(-1); + int hour = parser.nextIntValue(-1); + int minute = parser.nextIntValue(-1); + + t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + result = LocalDateTime.of(year, month, day, hour, minute); + } else { + int second = parser.getIntValue(); + t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + result = LocalDateTime.of(year, month, day, hour, minute, second); + } else { + int partialSecond = parser.getIntValue(); + if (partialSecond < 1_000 && !shouldReadTimestampsAsNanoseconds(context)) + partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds + if (parser.nextToken() != JsonToken.END_ARRAY) { + throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, + "Expected array to end"); + } + result = LocalDateTime.of(year, month, day, hour, minute, second, partialSecond); + } + } + return result; + } + context.reportInputMismatch(handledType(), + "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT", + t); + } + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (LocalDateTime) parser.getEmbeddedObject(); + } + if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + _throwNoNumericTimestampNeedTimeZone(parser, context); + } + return _handleUnexpectedToken(context, parser, "Expected array or string."); + } + + protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) { + return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride : + context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + protected LocalDateTime _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + final DateTimeFormatter format = _formatter; + try { + // 21-Oct-2020, tatu: Changed as per [modules-base#94] for 2.12, + // had bad timezone handle change from [modules-base#56] + if (_formatter == DEFAULT_FORMATTER) { + // ... only allow iff lenient mode enabled since + // JavaScript by default includes time and zone in JSON serialized Dates (UTC/ISO instant format). + if (string.length() > 10 && string.charAt(10) == 'T') { + if (string.endsWith("Z")) { + if (isLenient()) { + if (_useTimeZoneForLenientDateParsing) { + return Instant.parse(string).atZone(ctxt.getTimeZone().toZoneId()).toLocalDateTime(); + } + return LocalDateTime.parse(string.substring(0, string.length()-1), + _formatter); + } + JavaType t = getValueType(ctxt); + return (LocalDateTime) ctxt.handleWeirdStringValue(t.getRawClass(), + string, +"Should not contain offset when 'strict' mode set for property or type (enable 'lenient' handling to allow)" + ); + } + } + } + return LocalDateTime.parse(string, _formatter); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, format, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/LocalTimeDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/LocalTimeDeserializer.java new file mode 100644 index 0000000000..ca8c88e287 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/LocalTimeDeserializer.java @@ -0,0 +1,196 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; + +/** + * Deserializer for Java 8 temporal {@link LocalTime}s. + * + * @author Nick Williams + */ +public class LocalTimeDeserializer extends JSR310DateTimeDeserializerBase +{ + private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_TIME; + + public static final LocalTimeDeserializer INSTANCE = new LocalTimeDeserializer(); + + /** + * Flag for JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + * + * @since 2.16 + */ + protected final Boolean _readTimestampsAsNanosOverride; + + protected LocalTimeDeserializer() { // was private before 2.12 + this(DEFAULT_FORMATTER); + } + + public LocalTimeDeserializer(DateTimeFormatter formatter) { + super(LocalTime.class, formatter); + _readTimestampsAsNanosOverride = null; + } + + protected LocalTimeDeserializer(LocalTimeDeserializer base, Boolean leniency) { + super(base, leniency); + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + } + + /** + * Since 2.16 + */ + protected LocalTimeDeserializer(LocalTimeDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape, + Boolean readTimestampsAsNanosOverride) { + super(base, leniency, formatter, shape); + _readTimestampsAsNanosOverride = readTimestampsAsNanosOverride; + } + + @Override + protected LocalTimeDeserializer withDateFormat(DateTimeFormatter dtf) { + return new LocalTimeDeserializer(this, _isLenient, dtf, _shape, _readTimestampsAsNanosOverride); + } + + @Override + protected LocalTimeDeserializer withLeniency(Boolean leniency) { + return new LocalTimeDeserializer(this, leniency); + } + + @Override + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) + { + LocalTimeDeserializer deser = (LocalTimeDeserializer) + super._withFormatOverrides(ctxt, property, formatOverrides); + Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature( + JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + if (!Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) { + return new LocalTimeDeserializer(deser, deser._isLenient, deser._formatter, + deser._shape, readTimestampsAsNanosOverride); + } + return deser; + } + + @Override + public LocalTime deserialize(JsonParser parser, DeserializationContext context) throws JacksonException + { + if (parser.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (parser.isExpectedStartArrayToken()) { + JsonToken t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if (context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + && (t == JsonToken.VALUE_STRING || t==JsonToken.VALUE_EMBEDDED_OBJECT)) { + final LocalTime parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + if (t == JsonToken.VALUE_NUMBER_INT) { + int hour = parser.getIntValue(); + + parser.nextToken(); + int minute = parser.getIntValue(); + LocalTime result; + + t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + result = LocalTime.of(hour, minute); + } else { + int second = parser.getIntValue(); + t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + result = LocalTime.of(hour, minute, second); + } else { + int partialSecond = parser.getIntValue(); + if(partialSecond < 1_000 && !shouldReadTimestampsAsNanoseconds(context)) + partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds + t = parser.nextToken(); + if (t != JsonToken.END_ARRAY) { + throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, + "Expected array to end"); + } + result = LocalTime.of(hour, minute, second, partialSecond); + } + } + return result; + } + context.reportInputMismatch(handledType(), + "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT", + t); + } + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (LocalTime) parser.getEmbeddedObject(); + } + if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + _throwNoNumericTimestampNeedTimeZone(parser, context); + } + return _handleUnexpectedToken(context, parser, "Expected array or string."); + } + + protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) { + return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride : + context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + protected LocalTime _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + final DateTimeFormatter format = _formatter; + try { + if (format == DEFAULT_FORMATTER) { + if (string.contains("T")) { + return LocalTime.parse(string, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + } + return LocalTime.parse(string, format); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, format, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/MonthDayDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/MonthDayDeserializer.java new file mode 100644 index 0000000000..513a096427 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/MonthDayDeserializer.java @@ -0,0 +1,129 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.MonthDay; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; + +/** + * Deserializer for Java 8 temporal {@link MonthDay}s. + */ +public class MonthDayDeserializer extends JSR310DateTimeDeserializerBase +{ + public static final MonthDayDeserializer INSTANCE = new MonthDayDeserializer(); + + /** + * NOTE: only {@code public} so that use via annotations (see [modules-java8#202]) + * is possible + */ + public MonthDayDeserializer() { + this(null); + } + + public MonthDayDeserializer(DateTimeFormatter formatter) { + super(MonthDay.class, formatter); + } + + /** + * Since 2.12 + */ + protected MonthDayDeserializer(MonthDayDeserializer base, Boolean leniency) { + super(base, leniency); + } + + /** + * Since 2.16 + */ + protected MonthDayDeserializer(MonthDayDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape) { + super(base, leniency, formatter, shape); + } + + @Override + protected MonthDayDeserializer withLeniency(Boolean leniency) { + return new MonthDayDeserializer(this, leniency); + } + + @Override + protected MonthDayDeserializer withDateFormat(DateTimeFormatter dtf) { + return new MonthDayDeserializer(this, _isLenient, dtf, _shape); + } + + @Override + public MonthDay deserialize(JsonParser parser, DeserializationContext context) + throws JacksonException + { + if (parser.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (parser.isExpectedStartArrayToken()) { + JsonToken t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT) + && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + final MonthDay parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + if (t != JsonToken.VALUE_NUMBER_INT) { + _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "month"); + } + int month = parser.getIntValue(); + int day = parser.nextIntValue(-1); + if (day == -1) { + if (!parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "day"); + } + day = parser.getIntValue(); + } + if (parser.nextToken() != JsonToken.END_ARRAY) { + throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, + "Expected array to end"); + } + return MonthDay.of(month, day); + } + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (MonthDay) parser.getEmbeddedObject(); + } + return _handleUnexpectedToken(context, parser, + JsonToken.VALUE_STRING, JsonToken.START_ARRAY); + } + + protected MonthDay _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + try { + if (_formatter == null) { + return MonthDay.parse(string); + } + return MonthDay.parse(string, _formatter); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, _formatter, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserializer.java new file mode 100644 index 0000000000..1b472e0dfe --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserializer.java @@ -0,0 +1,195 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.*; +import tools.jackson.databind.*; + +/** + * Deserializer for Java 8 temporal {@link OffsetTime}s. + * + * @author Nick Williams + */ +public class OffsetTimeDeserializer extends JSR310DateTimeDeserializerBase +{ + public static final OffsetTimeDeserializer INSTANCE = new OffsetTimeDeserializer(); + + /** + * Flag for JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + * + * @since 2.16 + */ + protected final Boolean _readTimestampsAsNanosOverride; + + protected OffsetTimeDeserializer() { // was private before 2.12 + this(DateTimeFormatter.ISO_OFFSET_TIME); + } + + protected OffsetTimeDeserializer(DateTimeFormatter dtf) { + super(OffsetTime.class, dtf); + _readTimestampsAsNanosOverride = null; + } + + /** + * Since 2.11 + */ + protected OffsetTimeDeserializer(OffsetTimeDeserializer base, Boolean leniency) { + super(base, leniency); + _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; + } + + /** + * Since 2.16 + */ + protected OffsetTimeDeserializer(OffsetTimeDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape, + Boolean readTimestampsAsNanosOverride) { + super(base, leniency, formatter, shape); + _readTimestampsAsNanosOverride = readTimestampsAsNanosOverride; + } + + @Override + protected OffsetTimeDeserializer withDateFormat(DateTimeFormatter dtf) { + return new OffsetTimeDeserializer(this, _isLenient, dtf, _shape, _readTimestampsAsNanosOverride); + } + + @Override + protected OffsetTimeDeserializer withLeniency(Boolean leniency) { + return new OffsetTimeDeserializer(this, leniency); + } + + @Override + protected JSR310DateTimeDeserializerBase _withFormatOverrides(DeserializationContext ctxt, + BeanProperty property, JsonFormat.Value formatOverrides) + { + OffsetTimeDeserializer deser = (OffsetTimeDeserializer) + super._withFormatOverrides(ctxt, property, formatOverrides); + Boolean readTimestampsAsNanosOverride = formatOverrides.getFeature( + JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + if (!Objects.equals(readTimestampsAsNanosOverride, deser._readTimestampsAsNanosOverride)) { + return new OffsetTimeDeserializer(deser, deser._isLenient, deser._formatter, + deser._shape, readTimestampsAsNanosOverride); + } + return deser; + } + + @Override + public OffsetTime deserialize(JsonParser parser, DeserializationContext context) + throws JacksonException + { + if (parser.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (!parser.isExpectedStartArrayToken()) { + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (OffsetTime) parser.getEmbeddedObject(); + } + if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + _throwNoNumericTimestampNeedTimeZone(parser, context); + } + throw context.wrongTokenException(parser, handledType(), JsonToken.START_ARRAY, + "Expected array or string."); + } + JsonToken t = parser.nextToken(); + if (t != JsonToken.VALUE_NUMBER_INT) { + if (t == JsonToken.END_ARRAY) { + return null; + } + if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT) + && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + final OffsetTime parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + context.reportInputMismatch(handledType(), + "Unexpected token (%s) within Array, expected VALUE_NUMBER_INT", + t); + } + int hour = parser.getIntValue(); + int minute = parser.nextIntValue(-1); + if (minute == -1) { + t = parser.currentToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if (t != JsonToken.VALUE_NUMBER_INT) { + _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "minutes"); + } + minute = parser.getIntValue(); + } + int partialSecond = 0; + int second = 0; + if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) { + second = parser.getIntValue(); + if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) { + partialSecond = parser.getIntValue(); + if (partialSecond < 1_000 && !shouldReadTimestampsAsNanoseconds(context)) { + partialSecond *= 1_000_000; // value is milliseconds, convert it to nanoseconds + } + parser.nextToken(); + } + } + if (parser.currentToken() == JsonToken.VALUE_STRING) { + OffsetTime result = OffsetTime.of(hour, minute, second, partialSecond, ZoneOffset.of(parser.getString())); + if (parser.nextToken() != JsonToken.END_ARRAY) { + _reportWrongToken(context, JsonToken.END_ARRAY, "timezone"); + } + return result; + } + throw context.wrongTokenException(parser, handledType(), JsonToken.VALUE_STRING, + "Expected string for TimeZone after numeric values"); + } + + protected boolean shouldReadTimestampsAsNanoseconds(DeserializationContext context) { + return (_readTimestampsAsNanosOverride != null) ? _readTimestampsAsNanosOverride : + context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + protected OffsetTime _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + try { + return OffsetTime.parse(string, _formatter); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, _formatter, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserializer.java new file mode 100644 index 0000000000..561f9dc7ee --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserializer.java @@ -0,0 +1,48 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Month; +import java.util.regex.Pattern; + +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; + +import tools.jackson.databind.*; +import tools.jackson.databind.deser.std.DelegatingDeserializer; +import tools.jackson.databind.exc.InvalidFormatException; + +/** + * @since 2.17 + */ +public class OneBasedMonthDeserializer extends DelegatingDeserializer { + private static final Pattern HAS_ONE_OR_TWO_DIGITS = Pattern.compile("^\\d{1,2}$"); + + public OneBasedMonthDeserializer(ValueDeserializer defaultDeserializer) { + super(defaultDeserializer); + } + + @Override + public Object deserialize(JsonParser parser, DeserializationContext context) { + JsonToken token = parser.currentToken(); + Month zeroBaseMonth = (Month) getDelegatee().deserialize(parser, context); + if (!_isNumericValue(parser.getString(), token)) { + return zeroBaseMonth; + } + if (zeroBaseMonth == Month.JANUARY) { + throw new InvalidFormatException(parser, "Month.JANUARY value not allowed for 1-based Month.", zeroBaseMonth, Month.class); + } + return zeroBaseMonth.minus(1); + } + + private boolean _isNumericValue(String text, JsonToken token) { + return token == JsonToken.VALUE_NUMBER_INT || _isNumberAsString(text, token); + } + + private boolean _isNumberAsString(String text, JsonToken token) { + return token == JsonToken.VALUE_STRING && HAS_ONE_OR_TWO_DIGITS.matcher(text).matches(); + } + + @Override + protected ValueDeserializer newDelegatingInstance(ValueDeserializer newDelegatee) { + return new OneBasedMonthDeserializer(newDelegatee); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/YearDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/YearDeserializer.java new file mode 100644 index 0000000000..281d17cafe --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/YearDeserializer.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.Year; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.StreamReadCapability; +import tools.jackson.core.io.NumberInput; +import tools.jackson.databind.DeserializationContext; + +/** + * Deserializer for Java 8 temporal {@link Year}s. + * + * @author Nick Williams + */ +public class YearDeserializer extends JSR310DateTimeDeserializerBase +{ + public static final YearDeserializer INSTANCE = new YearDeserializer(); + + /** + * NOTE: only {@code public} so that use via annotations (see [modules-java8#202]) + * is possible + */ + public YearDeserializer() { // public since 2.12 + this(null); + } + + public YearDeserializer(DateTimeFormatter formatter) { + super(Year.class, formatter); + } + + /** + * Since 2.12 + */ + protected YearDeserializer(YearDeserializer base, Boolean leniency) { + super(base, leniency); + } + + /** + * Since 2.16 + */ + public YearDeserializer(YearDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape) { + super(base, leniency, formatter, shape); + } + + @Override + protected YearDeserializer withDateFormat(DateTimeFormatter dtf) { + return new YearDeserializer(this, _isLenient, dtf, _shape); + } + + @Override + protected YearDeserializer withLeniency(Boolean leniency) { + return new YearDeserializer(this, leniency); + } + + @Override + public Year deserialize(JsonParser parser, DeserializationContext context) throws JacksonException + { + JsonToken t = parser.currentToken(); + if (t == JsonToken.VALUE_STRING) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (t == JsonToken.START_OBJECT) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (t == JsonToken.VALUE_NUMBER_INT) { + return _fromNumber(context, parser.getIntValue()); + } + if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { + return (Year) parser.getEmbeddedObject(); + } + if (parser.hasToken(JsonToken.START_ARRAY)){ + return _deserializeFromArray(parser, context); + } + return _handleUnexpectedToken(context, parser, JsonToken.VALUE_STRING, JsonToken.VALUE_NUMBER_INT); + } + + protected Year _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + // 30-Sep-2020: Should allow use of "Timestamp as String" for XML/CSV + if (ctxt.isEnabled(StreamReadCapability.UNTYPED_SCALARS) + && _isValidTimestampString(string)) { + return _fromNumber(ctxt, NumberInput.parseInt(string)); + } + try { + if (_formatter == null) { + return Year.parse(string); + } + return Year.parse(string, _formatter); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, _formatter, string); + } + } + + protected Year _fromNumber(DeserializationContext ctxt, int value) { + return Year.of(value); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/YearMonthDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/YearMonthDeserializer.java new file mode 100644 index 0000000000..8e8890e302 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/YearMonthDeserializer.java @@ -0,0 +1,146 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.DateTimeException; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.core.JsonToken; + +/** + * Deserializer for Java 8 temporal {@link YearMonth}s. + * + * @author Nick Williams + * @since 2.2.0 + */ +public class YearMonthDeserializer extends JSR310DateTimeDeserializerBase +{ + public static final YearMonthDeserializer INSTANCE = new YearMonthDeserializer(); + + /** + * NOTE: only {@code public} so that use via annotations (see [modules-java8#202]) + * is possible + */ + public YearMonthDeserializer() // public since 2.12 + { + this(DateTimeFormatter.ofPattern("u-MM")); + } + + public YearMonthDeserializer(DateTimeFormatter formatter) + { + super(YearMonth.class, formatter); + } + + /** + * Since 2.11 + */ + protected YearMonthDeserializer(YearMonthDeserializer base, Boolean leniency) { + super(base, leniency); + } + + /** + * Since 2.16 + */ + public YearMonthDeserializer(YearMonthDeserializer base, + Boolean leniency, + DateTimeFormatter formatter, + JsonFormat.Shape shape) { + super(base, leniency, formatter, shape); + } + + @Override + protected YearMonthDeserializer withDateFormat(DateTimeFormatter dtf) { + return new YearMonthDeserializer(this, _isLenient, dtf, _shape); + } + + @Override + protected YearMonthDeserializer withLeniency(Boolean leniency) { + return new YearMonthDeserializer(this, leniency); + } + + @Override + public YearMonth deserialize(JsonParser parser, DeserializationContext context) throws JacksonException + { + if (parser.hasToken(JsonToken.VALUE_STRING)) { + return _fromString(parser, context, parser.getString()); + } + // 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML) + if (parser.isExpectedStartObjectToken()) { + return _fromString(parser, context, + context.extractScalarFromObject(parser, this, handledType())); + } + if (parser.isExpectedStartArrayToken()) { + JsonToken t = parser.nextToken(); + if (t == JsonToken.END_ARRAY) { + return null; + } + if ((t == JsonToken.VALUE_STRING || t == JsonToken.VALUE_EMBEDDED_OBJECT) + && context.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) { + final YearMonth parsed = deserialize(parser, context); + if (parser.nextToken() != JsonToken.END_ARRAY) { + handleMissingEndArrayForSingle(parser, context); + } + return parsed; + } + if (t != JsonToken.VALUE_NUMBER_INT) { + _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "years"); + } + int year = parser.getIntValue(); + int month = parser.nextIntValue(-1); + if (month == -1) { + if (!parser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + _reportWrongToken(context, JsonToken.VALUE_NUMBER_INT, "months"); + } + month = parser.getIntValue(); + } + if (parser.nextToken() != JsonToken.END_ARRAY) { + throw context.wrongTokenException(parser, handledType(), JsonToken.END_ARRAY, + "Expected array to end"); + } + return YearMonth.of(year, month); + } + if (parser.hasToken(JsonToken.VALUE_EMBEDDED_OBJECT)) { + return (YearMonth) parser.getEmbeddedObject(); + } + return _handleUnexpectedToken(context, parser, + JsonToken.VALUE_STRING, JsonToken.START_ARRAY); + } + + protected YearMonth _fromString(JsonParser p, DeserializationContext ctxt, + String string0) + throws JacksonException + { + String string = string0.trim(); + if (string.length() == 0) { + // 22-Oct-2020, tatu: not sure if we should pass original (to distinguish + // b/w empty and blank); for now don't which will allow blanks to be + // handled like "regular" empty (same as pre-2.12) + return _fromEmptyString(p, ctxt, string); + } + try { + return YearMonth.parse(string, _formatter); + } catch (DateTimeException e) { + return _handleDateTimeFormatException(ctxt, e, _formatter, string); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/DurationKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/DurationKeyDeserializer.java new file mode 100644 index 0000000000..a2587224ce --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/DurationKeyDeserializer.java @@ -0,0 +1,27 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.Duration; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class DurationKeyDeserializer extends Jsr310KeyDeserializer { + + public static final DurationKeyDeserializer INSTANCE = new DurationKeyDeserializer(); + + private DurationKeyDeserializer() { + // singleton + } + + @Override + protected Duration deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return Duration.parse(key); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, Duration.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/InstantKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/InstantKeyDeserializer.java new file mode 100644 index 0000000000..e625c34abb --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/InstantKeyDeserializer.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class InstantKeyDeserializer extends Jsr310KeyDeserializer { + + public static final InstantKeyDeserializer INSTANCE = new InstantKeyDeserializer(); + + private InstantKeyDeserializer() { + // singleton + } + + @Override + protected Instant deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return DateTimeFormatter.ISO_INSTANT.parse(key, Instant::from); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, Instant.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/Jsr310KeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/Jsr310KeyDeserializer.java new file mode 100644 index 0000000000..1440529d5d --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/Jsr310KeyDeserializer.java @@ -0,0 +1,41 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.KeyDeserializer; +import tools.jackson.databind.util.ClassUtil; + +abstract class Jsr310KeyDeserializer extends KeyDeserializer +{ + @Override + public final Object deserializeKey(String key, DeserializationContext ctxt) + throws JacksonException + { + // 17-Aug-2019, tatu: Jackson 2.x had special handling for "null" key marker, which + // is why we have this unnecessary dispatching, for now + return deserialize(key, ctxt); + } + + protected abstract Object deserialize(String key, DeserializationContext ctxt) + throws JacksonException; + + @SuppressWarnings("unchecked") + protected T _handleDateTimeException(DeserializationContext ctxt, + Class type, DateTimeException e0, String value) + throws JacksonException + { + try { + return (T) ctxt.handleWeirdKey(type, value, + "Failed to deserialize %s: (%s) %s", + ClassUtil.nameOf(type), + e0.getClass().getName(), + e0.getMessage()); + + } catch (JacksonException e) { + e.initCause(e0); + throw e; + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateKeyDeserializer.java new file mode 100644 index 0000000000..2810bfe7f6 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateKeyDeserializer.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class LocalDateKeyDeserializer extends Jsr310KeyDeserializer { + + public static final LocalDateKeyDeserializer INSTANCE = new LocalDateKeyDeserializer(); + + private LocalDateKeyDeserializer() { + // singleton + } + + @Override + protected LocalDate deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return LocalDate.parse(key, DateTimeFormatter.ISO_LOCAL_DATE); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, LocalDate.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateTimeKeyDeserializer.java new file mode 100644 index 0000000000..e4370de9d2 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalDateTimeKeyDeserializer.java @@ -0,0 +1,29 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class LocalDateTimeKeyDeserializer extends Jsr310KeyDeserializer { + + public static final LocalDateTimeKeyDeserializer INSTANCE = new LocalDateTimeKeyDeserializer(); + + private LocalDateTimeKeyDeserializer() { + // singleton + } + + @Override + protected LocalDateTime deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return LocalDateTime.parse(key, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, LocalDateTime.class, e, key); + } + } + +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/LocalTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalTimeKeyDeserializer.java new file mode 100644 index 0000000000..e96038c08d --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/LocalTimeKeyDeserializer.java @@ -0,0 +1,29 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class LocalTimeKeyDeserializer extends Jsr310KeyDeserializer { + + public static final LocalTimeKeyDeserializer INSTANCE = new LocalTimeKeyDeserializer(); + + private LocalTimeKeyDeserializer() { + // singleton + } + + @Override + protected LocalTime deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return LocalTime.parse(key, DateTimeFormatter.ISO_LOCAL_TIME); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, LocalTime.class, e, key); + } + } + +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/MonthDayKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/MonthDayKeyDeserializer.java new file mode 100644 index 0000000000..fba9eb4c36 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/MonthDayKeyDeserializer.java @@ -0,0 +1,40 @@ +package tools.jackson.databind.datetime.deser.key; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; + +import java.time.DateTimeException; +import java.time.MonthDay; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class MonthDayKeyDeserializer extends Jsr310KeyDeserializer { + + public static final MonthDayKeyDeserializer INSTANCE = new MonthDayKeyDeserializer(); + + // formatter copied from MonthDay + private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .appendLiteral("--") + .appendValue(MONTH_OF_YEAR, 2) + .appendLiteral('-') + .appendValue(DAY_OF_MONTH, 2) + .toFormatter(); + + private MonthDayKeyDeserializer() { + // singleton + } + + @Override + protected MonthDay deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return MonthDay.parse(key, PARSER); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, MonthDay.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetDateTimeKeyDeserializer.java new file mode 100644 index 0000000000..d03abf98ce --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetDateTimeKeyDeserializer.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class OffsetDateTimeKeyDeserializer extends Jsr310KeyDeserializer { + + public static final OffsetDateTimeKeyDeserializer INSTANCE = new OffsetDateTimeKeyDeserializer(); + + private OffsetDateTimeKeyDeserializer() { + // singleton + } + + @Override + protected OffsetDateTime deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return OffsetDateTime.parse(key, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, OffsetDateTime.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetTimeKeyDeserializer.java new file mode 100644 index 0000000000..c7aa3ff869 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/OffsetTimeKeyDeserializer.java @@ -0,0 +1,29 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.OffsetTime; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class OffsetTimeKeyDeserializer extends Jsr310KeyDeserializer { + + public static final OffsetTimeKeyDeserializer INSTANCE = new OffsetTimeKeyDeserializer(); + + private OffsetTimeKeyDeserializer() { + // singleton + } + + @Override + protected OffsetTime deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return OffsetTime.parse(key, DateTimeFormatter.ISO_OFFSET_TIME); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, OffsetTime.class, e, key); + } + } + +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/PeriodKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/PeriodKeyDeserializer.java new file mode 100644 index 0000000000..b4fa4a948f --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/PeriodKeyDeserializer.java @@ -0,0 +1,27 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.Period; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class PeriodKeyDeserializer extends Jsr310KeyDeserializer { + + public static final PeriodKeyDeserializer INSTANCE = new PeriodKeyDeserializer(); + + private PeriodKeyDeserializer() { + // singletin + } + + @Override + protected Period deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return Period.parse(key); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, Period.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/YearKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/YearKeyDeserializer.java new file mode 100644 index 0000000000..5b4ea69962 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/YearKeyDeserializer.java @@ -0,0 +1,29 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.Year; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class YearKeyDeserializer extends Jsr310KeyDeserializer { + + public static final YearKeyDeserializer INSTANCE = new YearKeyDeserializer(); + + private YearKeyDeserializer() { + // singleton + } + + @Override + protected Year deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return Year.of(Integer.parseInt(key)); + } catch (NumberFormatException nfe) { + return _handleDateTimeException(ctxt, Year.class, new DateTimeException("Number format exception", nfe), key); + } catch (DateTimeException dte) { + return _handleDateTimeException(ctxt, Year.class, dte, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/YearMonthKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/YearMonthKeyDeserializer.java new file mode 100644 index 0000000000..812f2609e9 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/YearMonthKeyDeserializer.java @@ -0,0 +1,37 @@ +package tools.jackson.databind.datetime.deser.key; + +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; + +import java.time.DateTimeException; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.SignStyle; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class YearMonthKeyDeserializer extends Jsr310KeyDeserializer { + public static final YearMonthKeyDeserializer INSTANCE = new YearMonthKeyDeserializer(); + + // parser copied from YearMonth + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(MONTH_OF_YEAR, 2) + .toFormatter(); + + private YearMonthKeyDeserializer() { } // singleton + + @Override + protected YearMonth deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return YearMonth.parse(key, FORMATTER); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, YearMonth.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneIdKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneIdKeyDeserializer.java new file mode 100644 index 0000000000..afebe6e3df --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneIdKeyDeserializer.java @@ -0,0 +1,27 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.ZoneId; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class ZoneIdKeyDeserializer extends Jsr310KeyDeserializer { + + public static final ZoneIdKeyDeserializer INSTANCE = new ZoneIdKeyDeserializer(); + + private ZoneIdKeyDeserializer() { + // singleton + } + + @Override + protected Object deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return ZoneId.of(key); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, ZoneId.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneOffsetKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneOffsetKeyDeserializer.java new file mode 100644 index 0000000000..2bd65786c4 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/ZoneOffsetKeyDeserializer.java @@ -0,0 +1,27 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.ZoneOffset; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class ZoneOffsetKeyDeserializer extends Jsr310KeyDeserializer { + + public static final ZoneOffsetKeyDeserializer INSTANCE = new ZoneOffsetKeyDeserializer(); + + private ZoneOffsetKeyDeserializer() { + // singleton + } + + @Override + protected ZoneOffset deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + return ZoneOffset.of(key); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, ZoneOffset.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializer.java new file mode 100644 index 0000000000..64ba5b78f6 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializer.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.DateTimeException; +import java.time.ZonedDateTime; + +import tools.jackson.core.JacksonException; +import tools.jackson.databind.DeserializationContext; + +public class ZonedDateTimeKeyDeserializer extends Jsr310KeyDeserializer { + + public static final ZonedDateTimeKeyDeserializer INSTANCE = new ZonedDateTimeKeyDeserializer(); + + protected ZonedDateTimeKeyDeserializer() { + // singleton + } + + @Override + protected ZonedDateTime deserialize(String key, DeserializationContext ctxt) + throws JacksonException + { + try { + // Not supplying a formatter allows the use of all supported formats + return ZonedDateTime.parse(key); + } catch (DateTimeException e) { + return _handleDateTimeException(ctxt, ZonedDateTime.class, e, key); + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/DurationSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/DurationSerializer.java new file mode 100644 index 0000000000..e46efb059b --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/DurationSerializer.java @@ -0,0 +1,185 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; + +import tools.jackson.databind.*; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; +import tools.jackson.databind.datetime.util.DecimalUtils; +import tools.jackson.databind.datetime.util.DurationUnitConverter; + +/** + * Serializer for Java 8 temporal {@link Duration}s. + *

+ * NOTE: since 2.10, {@link SerializationFeature#WRITE_DURATIONS_AS_TIMESTAMPS} + * determines global default used for determining if serialization should use + * numeric (timestamps) or textual representation. Before this, + * {@link SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} was used. + * + * @author Nick Williams + */ +public class DurationSerializer extends JSR310FormattedSerializerBase +{ + public static final DurationSerializer INSTANCE = new DurationSerializer(); + + /** + * When defined (not {@code null}) duration values will be converted into integers + * with the unit configured for the converter. + * Only available when {@link SerializationFeature#WRITE_DURATIONS_AS_TIMESTAMPS} is enabled + * and {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} is not enabled + * since the duration converters do not support fractions + * @since 2.12 + */ + private DurationUnitConverter _durationUnitConverter; + + protected DurationSerializer() { // was private before 2.12 + super(Duration.class); + } + + protected DurationSerializer(DurationSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp) { + super(base, dtf, useTimestamp, null, null); + } + + protected DurationSerializer(DurationSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds) { + super(base, dtf, useTimestamp, useNanoseconds, null); + } + + protected DurationSerializer(DurationSerializer base, DurationUnitConverter converter) { + super(base, base._formatter, base._useTimestamp, base._useNanoseconds, base._shape); + _durationUnitConverter = converter; + } + + @Override + protected DurationSerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new DurationSerializer(this, dtf, useTimestamp); + } + + protected DurationSerializer withConverter(DurationUnitConverter converter) { + return new DurationSerializer(this, converter); + } + + // @since 2.10 + @Override + protected SerializationFeature getTimestampsFeature() { + return SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS; + } + + @Override + public ValueSerializer createContextual(SerializationContext ctxt, BeanProperty property) + { + DurationSerializer ser = (DurationSerializer) super.createContextual(ctxt, property); + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); + if (format != null && format.hasPattern()) { + final String pattern = format.getPattern(); + DurationUnitConverter p = DurationUnitConverter.from(pattern); + if (p == null) { + ctxt.reportBadDefinition(handledType(), + String.format( + "Bad 'pattern' definition (\"%s\") for `Duration`: expected one of [%s]", + pattern, DurationUnitConverter.descForAllowed())); + } + ser = ser.withConverter(p); + } + return ser; + } + + @Override + public void serialize(Duration duration, JsonGenerator generator, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + // 03-Aug-2022, tatu: As per [modules-java8#224] need to consider + // Pattern first, and only then nano-seconds/millis difference + if (_durationUnitConverter != null) { + generator.writeNumber(_durationUnitConverter.convert(duration)); + } else if (useNanoseconds(ctxt)) { + generator.writeNumber(_toNanos(duration)); + } else { + generator.writeNumber(duration.toMillis()); + } + } else { + generator.writeString(duration.toString()); + } + } + + // 20-Oct-2020, tatu: [modules-java8#165] Need to take care of + // negative values too, and without work-around values + // returned are wonky wrt conversions + private BigDecimal _toNanos(Duration duration) { + BigDecimal bd; + if (duration.isNegative()) { + duration = duration.abs(); + bd = DecimalUtils.toBigDecimal(duration.getSeconds(), + duration.getNano()) + .negate(); + } else { + bd = DecimalUtils.toBigDecimal(duration.getSeconds(), + duration.getNano()); + } + return bd; + } + + @Override + protected void _acceptTimestampVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); + if (v2 != null) { + v2.numberType(JsonParser.NumberType.LONG); + SerializationContext ctxt = visitor.getContext(); + if ((ctxt != null) && useNanoseconds(ctxt)) { + // big number, no more specific qualifier to use... + } else { // otherwise good old Unix timestamp, in milliseconds + v2.format(JsonValueFormat.UTC_MILLISEC); + } + } + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + if (useTimestamp(ctxt)) { + if (useNanoseconds(ctxt)) { + return JsonToken.VALUE_NUMBER_FLOAT; + } + return JsonToken.VALUE_NUMBER_INT; + } + return JsonToken.VALUE_STRING; + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean writeNanoseconds) { + return new DurationSerializer(this, _formatter, _useTimestamp, writeNanoseconds); + } + + @Override + protected DateTimeFormatter _useDateTimeFormatter(SerializationContext ctxt, JsonFormat.Value format) { + return null; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializer.java new file mode 100644 index 0000000000..dc1fb475a0 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializer.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Serializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s. + * + * @author Nick Williams + */ +public class InstantSerializer extends InstantSerializerBase +{ + public static final InstantSerializer INSTANCE = new InstantSerializer(); + + protected InstantSerializer() { + super(Instant.class, Instant::toEpochMilli, Instant::getEpochSecond, Instant::getNano, + // null -> use 'value.toString()', default format + null); + } + + /* + protected InstantSerializer(InstantSerializer base, + Boolean useTimestamp, DateTimeFormatter formatter) { + this(base, formatter, useTimestamp, base._useNanoseconds, base); + } + */ + + protected InstantSerializer(InstantSerializer base, DateTimeFormatter formatter, + Boolean useTimestamp, Boolean useNanoseconds, + JsonFormat.Shape shape) { + super(base, formatter, useTimestamp, useNanoseconds, shape); + } + + @Override + protected JSR310FormattedSerializerBase withFormat(DateTimeFormatter formatter, + Boolean useTimestamp, + JsonFormat.Shape shape) { + return new InstantSerializer(this, formatter, useTimestamp, this._useNanoseconds , shape); + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean writeNanoseconds) { + return new InstantSerializer(this, _formatter, _useTimestamp, writeNanoseconds, + this._shape); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializerBase.java b/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializerBase.java new file mode 100644 index 0000000000..afc8cd69dc --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/InstantSerializerBase.java @@ -0,0 +1,147 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.JsonParser.NumberType; + +import tools.jackson.databind.*; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; +import tools.jackson.databind.datetime.util.DecimalUtils; + +/** + * Base class for serializers used for {@link java.time.Instant} and + * other {@link Temporal} subtypes. + */ +public abstract class InstantSerializerBase + extends JSR310FormattedSerializerBase +{ + private final DateTimeFormatter defaultFormat; + + private final ToLongFunction getEpochMillis; + + private final ToLongFunction getEpochSeconds; + + private final ToIntFunction getNanoseconds; + + protected InstantSerializerBase(Class supportedType, ToLongFunction getEpochMillis, + ToLongFunction getEpochSeconds, ToIntFunction getNanoseconds, + DateTimeFormatter defaultFormat) + { + // Bit complicated, just because we actually want to "hide" default formatter, + // so that it won't accidentally force use of textual presentation + super(supportedType, null); + this.defaultFormat = defaultFormat; + this.getEpochMillis = getEpochMillis; + this.getEpochSeconds = getEpochSeconds; + this.getNanoseconds = getNanoseconds; + } + + protected InstantSerializerBase(InstantSerializerBase base, + DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds, + JsonFormat.Shape shape) + { + super(base, dtf, useTimestamp, useNanoseconds, shape); + defaultFormat = base.defaultFormat; + getEpochMillis = base.getEpochMillis; + getEpochSeconds = base.getEpochSeconds; + getNanoseconds = base.getNanoseconds; + } + + @Override + protected abstract JSR310FormattedSerializerBase withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, + JsonFormat.Shape shape); + + @Override + public void serialize(T value, JsonGenerator generator, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + if (useNanoseconds(ctxt)) { + generator.writeNumber(DecimalUtils.toBigDecimal( + getEpochSeconds.applyAsLong(value), getNanoseconds.applyAsInt(value) + )); + return; + } + generator.writeNumber(getEpochMillis.applyAsLong(value)); + return; + } + + generator.writeString(formatValue(value, ctxt)); + } + + // Overridden to ensure that our timestamp handling is as expected + @Override + protected void _acceptTimestampVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + if (useNanoseconds(visitor.getContext())) { + JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint); + if (v2 != null) { + v2.numberType(NumberType.BIG_DECIMAL); + } + } else { + JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); + if (v2 != null) { + v2.numberType(NumberType.LONG); + v2.format(JsonValueFormat.UTC_MILLISEC); + } + } + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + if (useTimestamp(ctxt)) { + if (useNanoseconds(ctxt)) { + return JsonToken.VALUE_NUMBER_FLOAT; + } + return JsonToken.VALUE_NUMBER_INT; + } + return JsonToken.VALUE_STRING; + } + + protected String formatValue(T value, SerializationContext ctxt) + { + DateTimeFormatter formatter = (_formatter == null) ? defaultFormat :_formatter; + if (formatter != null) { + if (formatter.getZone() == null) { // timezone set if annotated on property + // If the user specified to use the context TimeZone explicitly, and the formatter provided doesn't contain a TZ + // Then we use the TZ specified in the objectMapper + if (ctxt.getConfig().hasExplicitTimeZone() + && ctxt.isEnabled(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE)) { + formatter = formatter.withZone(ctxt.getTimeZone().toZoneId()); + } + } + return formatter.format(value); + } + + return value.toString(); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/JSR310FormattedSerializerBase.java b/src/main/java/tools/jackson/databind/datetime/ser/JSR310FormattedSerializerBase.java new file mode 100644 index 0000000000..2499e6b3ef --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/JSR310FormattedSerializerBase.java @@ -0,0 +1,258 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Locale; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; + +import tools.jackson.databind.*; +import tools.jackson.databind.jsonFormatVisitors.*; + +/** + * Base class that provides an array schema instead of scalar schema if + * {@link SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} is enabled. + * + * @author Nick Williams + */ +abstract class JSR310FormattedSerializerBase + extends JSR310SerializerBase +{ + /** + * Flag that indicates that serialization must be done as the + * Java timestamp, regardless of other settings. + */ + protected final Boolean _useTimestamp; + + /** + * Flag that indicates that numeric timestamp values must be written using + * nanosecond timestamps if the datatype supports such resolution, + * regardless of other settings. + */ + protected final Boolean _useNanoseconds; + + /** + * Specific format to use, if not default format: non-null value + * also indicates that serialization is to be done as JSON String, + * not numeric timestamp, unless {@code #_useTimestamp} is true. + */ + protected final DateTimeFormatter _formatter; + + protected final JsonFormat.Shape _shape; + + /** + * Lazily constructed {@code JavaType} representing type + * {@code List}. + * + * @since 2.10 + */ + protected transient volatile JavaType _integerListType; + + protected JSR310FormattedSerializerBase(Class supportedType) { + this(supportedType, null); + } + + protected JSR310FormattedSerializerBase(Class supportedType, + DateTimeFormatter formatter) { + super(supportedType); + _useTimestamp = null; + _useNanoseconds = null; + _shape = null; + _formatter = formatter; + } + + /* + + protected JSR310FormattedSerializerBase(JSR310FormattedSerializerBase base, + DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) + { + this(base, dtf, useTimestamp, null, shape); + } + */ + + protected JSR310FormattedSerializerBase(JSR310FormattedSerializerBase base, + DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds, + JsonFormat.Shape shape) + { + super(base.handledType()); + _useTimestamp = useTimestamp; + _useNanoseconds = useNanoseconds; + _formatter = dtf; + _shape = shape; + } + + protected abstract JSR310FormattedSerializerBase withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape); + + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, + Boolean writeNanoseconds) { + return this; + } + + @Override + public ValueSerializer createContextual(SerializationContext ctxt, + BeanProperty property) + { + JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType()); + if (format != null) { + Boolean useTimestamp = null; + + // Simple case first: serialize as numeric timestamp? + JsonFormat.Shape shape = format.getShape(); + if (shape == JsonFormat.Shape.ARRAY || shape.isNumeric() ) { + useTimestamp = Boolean.TRUE; + } else { + useTimestamp = (shape == JsonFormat.Shape.STRING) ? Boolean.FALSE : null; + } + DateTimeFormatter dtf = _formatter; + + // If not, do we have a pattern? + if (format.hasPattern()) { + dtf = _useDateTimeFormatter(ctxt, format); + } + JSR310FormattedSerializerBase ser = this; + if ((shape != _shape) || (useTimestamp != _useTimestamp) || (dtf != _formatter)) { + ser = ser.withFormat(dtf, useTimestamp, shape); + } + Boolean writeZoneId = format.getFeature(JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID); + Boolean writeNanoseconds = format.getFeature(JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); + if ((writeZoneId != null) || (writeNanoseconds != null)) { + ser = ser.withFeatures(writeZoneId, writeNanoseconds); + } + return ser; + } + return this; + } + + /** + * @deprecated Since 2.15 + */ + @Deprecated + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + if (useTimestamp(visitor.getContext())) { + _acceptTimestampVisitor(visitor, typeHint); + } else { + JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint); + if (v2 != null) { + v2.format(JsonValueFormat.DATE_TIME); + } + } + } + + protected void _acceptTimestampVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + // By default, most sub-types use JSON Array, so do this: + // 28-May-2019, tatu: serialized as a List, presumably + JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(_integerListType(visitor.getContext())); + if (v2 != null) { + v2.itemsFormat(JsonFormatTypes.INTEGER); + } + } + + protected JavaType _integerListType(SerializationContext ctxt) { + JavaType t = _integerListType; + if (t == null) { + t = ctxt.getTypeFactory() + .constructCollectionType(List.class, Integer.class); + _integerListType = t; + } + return t; + } + + /** + * Overridable method that determines {@link SerializationFeature} that is used as + * the global default in determining if date/time value serialized should use numeric + * format ("timestamp") or not. + *

+ * Note that this feature is just the baseline setting and may be overridden on per-type + * or per-property basis. + * + * @since 2.10 + */ + protected SerializationFeature getTimestampsFeature() { + return SerializationFeature.WRITE_DATES_AS_TIMESTAMPS; + } + + protected boolean useTimestamp(SerializationContext ctxt) { + if (_useTimestamp != null) { + return _useTimestamp.booleanValue(); + } + if (_shape != null) { + if (_shape == Shape.STRING) { + return false; + } + if (_shape == Shape.NUMBER_INT) { + return true; + } + } + // assume that explicit formatter definition implies use of textual format + return (_formatter == null) && useTimestampFromGlobalDefaults(ctxt); + } + + protected boolean useTimestampFromGlobalDefaults(SerializationContext ctxt) { + return (ctxt != null) + && ctxt.isEnabled(getTimestampsFeature()); + } + + protected boolean _useTimestampExplicitOnly(SerializationContext ctxt) { + if (_useTimestamp != null) { + return _useTimestamp.booleanValue(); + } + return false; + } + + protected boolean useNanoseconds(SerializationContext ctxt) { + if (_useNanoseconds != null) { + return _useNanoseconds.booleanValue(); + } + if (_shape != null) { + if (_shape == Shape.NUMBER_INT) { + return false; + } + if (_shape == Shape.NUMBER_FLOAT) { + return true; + } + } + return (ctxt != null) + && ctxt.isEnabled(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + // modules-java8#189: to be overridden by other formatters using this as base class + protected DateTimeFormatter _useDateTimeFormatter(SerializationContext ctxt, JsonFormat.Value format) { + DateTimeFormatter dtf; + final String pattern = format.getPattern(); + final Locale locale = format.hasLocale() ? format.getLocale() : ctxt.getLocale(); + if (locale == null) { + dtf = DateTimeFormatter.ofPattern(pattern); + } else { + dtf = DateTimeFormatter.ofPattern(pattern, locale); + } + //Issue #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()) { + dtf = dtf.withZone(format.getTimeZone().toZoneId()); + } + return dtf; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/JSR310SerializerBase.java b/src/main/java/tools/jackson/databind/datetime/ser/JSR310SerializerBase.java new file mode 100644 index 0000000000..06e0131eeb --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/JSR310SerializerBase.java @@ -0,0 +1,40 @@ +package tools.jackson.databind.datetime.ser; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; + +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; +import tools.jackson.databind.ser.std.StdSerializer; + +/** + * Base class that indicates that all JSR310 datatypes are serialized as scalar JSON types. + * + * @author Nick Williams + */ +abstract class JSR310SerializerBase extends StdSerializer +{ + protected JSR310SerializerBase(Class supportedType) { + super(supportedType); + } + + @Override + public void serializeWithType(T value, JsonGenerator g, SerializationContext ctxt, + TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + serialize(value, g, ctxt); + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + /** + * Overridable helper method used from {@link #serializeWithType}, to indicate + * shape of value during serialization; needed to know how type id is to be + * serialized. + */ + protected abstract JsonToken serializationShape(SerializationContext ctxt); +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/JavaTimeSerializerModifier.java b/src/main/java/tools/jackson/databind/datetime/ser/JavaTimeSerializerModifier.java new file mode 100644 index 0000000000..5de050859d --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/JavaTimeSerializerModifier.java @@ -0,0 +1,28 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Month; + +import tools.jackson.databind.*; +import tools.jackson.databind.ser.ValueSerializerModifier; + +/** + * @since 2.17 + */ +public class JavaTimeSerializerModifier extends ValueSerializerModifier { + private static final long serialVersionUID = 1L; + + private final boolean _oneBaseMonths; + + public JavaTimeSerializerModifier(boolean oneBaseMonths) { + _oneBaseMonths = oneBaseMonths; + } + + @Override + public ValueSerializer modifyEnumSerializer(SerializationConfig config, JavaType valueType, + BeanDescription beanDesc, ValueSerializer serializer) { + if (_oneBaseMonths && valueType.hasRawClass(Month.class)) { + return new OneBasedMonthSerializer(serializer); + } + return serializer; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/LocalDateSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/LocalDateSerializer.java new file mode 100644 index 0000000000..b798806f00 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/LocalDateSerializer.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; +import tools.jackson.databind.*; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; +import tools.jackson.databind.jsontype.TypeSerializer; + +/** + * Serializer for Java 8 temporal {@link LocalDate}s. + * + * @author Nick Williams + */ +public class LocalDateSerializer extends JSR310FormattedSerializerBase +{ + public static final LocalDateSerializer INSTANCE = new LocalDateSerializer(); + + protected LocalDateSerializer() { + super(LocalDate.class); + } + + protected LocalDateSerializer(LocalDateSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + super(base, dtf, useTimestamp, null, shape); + } + + public LocalDateSerializer(DateTimeFormatter formatter) { + super(LocalDate.class, formatter); + } + + @Override + protected LocalDateSerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new LocalDateSerializer(this, dtf, useTimestamp, shape); + } + + @Override + public void serialize(LocalDate date, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + if (_shape == JsonFormat.Shape.NUMBER_INT) { + g.writeNumber(date.toEpochDay()); + } else { + g.writeStartArray(); + _serializeAsArrayContents(date, g, ctxt); + g.writeEndArray(); + } + } else { + g.writeString((_formatter == null) ? date.toString() : date.format(_formatter)); + } + } + + @Override + public void serializeWithType(LocalDate value, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + JsonToken shape = (typeIdDef == null) ? null : typeIdDef.valueShape; + if (shape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else if (shape == JsonToken.VALUE_NUMBER_INT) { + g.writeNumber(value.toEpochDay()); + } else { + g.writeString((_formatter == null) ? value.toString() : value.format(_formatter)); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + protected void _serializeAsArrayContents(LocalDate value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getYear()); + g.writeNumber(value.getMonthValue()); + g.writeNumber(value.getDayOfMonth()); + } + + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + SerializationContext ctxt = visitor.getContext(); + boolean useTimestamp = (ctxt != null) && useTimestamp(ctxt); + if (useTimestamp) { + _acceptTimestampVisitor(visitor, typeHint); + } else { + JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint); + if (v2 != null) { + v2.format(JsonValueFormat.DATE); + } + } + } + + @Override // since 2.9 + protected JsonToken serializationShape(SerializationContext ctxt) { + if (useTimestamp(ctxt)) { + if (_shape == JsonFormat.Shape.NUMBER_INT) { + return JsonToken.VALUE_NUMBER_INT; + } + return JsonToken.START_ARRAY; + } + return JsonToken.VALUE_STRING; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerializer.java new file mode 100644 index 0000000000..d450df3b9c --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerializer.java @@ -0,0 +1,133 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; + +/** + * Serializer for Java 8 temporal {@link LocalDateTime}s. + * + * @author Nick Williams + */ +public class LocalDateTimeSerializer extends JSR310FormattedSerializerBase +{ + public static final LocalDateTimeSerializer INSTANCE = new LocalDateTimeSerializer(); + + protected LocalDateTimeSerializer() { + this(null); + } + + public LocalDateTimeSerializer(DateTimeFormatter f) { + super(LocalDateTime.class, f); + } + + protected LocalDateTimeSerializer(LocalDateTimeSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds) { + super(base, dtf, useTimestamp, useNanoseconds, null); + } + + @Override + protected JSR310FormattedSerializerBase withFormat(DateTimeFormatter f, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new LocalDateTimeSerializer(this, f, useTimestamp, _useNanoseconds); + } + + protected DateTimeFormatter _defaultFormatter() { + return DateTimeFormatter.ISO_LOCAL_DATE_TIME; + } + + @Override + public void serialize(LocalDateTime value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + g.writeStartArray(); + _serializeAsArrayContents(value, g, ctxt); + g.writeEndArray(); + } else { + DateTimeFormatter dtf = _formatter; + if (dtf == null) { + dtf = _defaultFormatter(); + } + g.writeString(value.format(dtf)); + } + } + + @Override + public void serializeWithType(LocalDateTime value, JsonGenerator g, SerializationContext ctxt, + TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + if ((typeIdDef != null) + && typeIdDef.valueShape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else { + DateTimeFormatter dtf = _formatter; + if (dtf == null) { + dtf = _defaultFormatter(); + } + g.writeString(value.format(dtf)); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + private final void _serializeAsArrayContents(LocalDateTime value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getYear()); + g.writeNumber(value.getMonthValue()); + g.writeNumber(value.getDayOfMonth()); + g.writeNumber(value.getHour()); + g.writeNumber(value.getMinute()); + final int secs = value.getSecond(); + final int nanos = value.getNano(); + if ((secs > 0) || (nanos > 0)) { + g.writeNumber(secs); + if (nanos > 0) { + if (useNanoseconds(ctxt)) { + g.writeNumber(nanos); + } else { + g.writeNumber(value.get(ChronoField.MILLI_OF_SECOND)); + } + } + } + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + return useTimestamp(ctxt) ? JsonToken.START_ARRAY : JsonToken.VALUE_STRING; + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean writeNanoseconds) { + return new LocalDateTimeSerializer(this, _formatter, _useTimestamp, writeNanoseconds); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/LocalTimeSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/LocalTimeSerializer.java new file mode 100644 index 0000000000..a0b82526ac --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/LocalTimeSerializer.java @@ -0,0 +1,153 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; + +import tools.jackson.databind.JavaType; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; +import tools.jackson.databind.jsontype.TypeSerializer; +/** + * Serializer for Java 8 temporal {@link LocalTime}s. + * + * @author Nick Williams + * @since 2.2 + */ +public class LocalTimeSerializer extends JSR310FormattedSerializerBase +{ + public static final LocalTimeSerializer INSTANCE = new LocalTimeSerializer(); + + protected LocalTimeSerializer() { + this(null); + } + + public LocalTimeSerializer(DateTimeFormatter formatter) { + super(LocalTime.class, formatter); + } + + protected LocalTimeSerializer(LocalTimeSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds) { + super(base, dtf, useTimestamp, useNanoseconds, null); + } + + @Override + protected JSR310FormattedSerializerBase withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new LocalTimeSerializer(this, dtf, useTimestamp, _useNanoseconds); + } + + // since 2.7: TODO in 3.x; change to use per-type defaulting + protected DateTimeFormatter _defaultFormatter() { + return DateTimeFormatter.ISO_LOCAL_TIME; + } + + @Override + public void serialize(LocalTime value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + g.writeStartArray(); + _serializeAsArrayContents(value, g, ctxt); + g.writeEndArray(); + } else { + DateTimeFormatter dtf = _formatter; + if (dtf == null) { + dtf = _defaultFormatter(); + } + g.writeString(value.format(dtf)); + } + } + + @Override + public void serializeWithType(LocalTime value, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + if ((typeIdDef != null) + && typeIdDef.valueShape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else { + DateTimeFormatter dtf = _formatter; + if (dtf == null) { + dtf = _defaultFormatter(); + } + g.writeString(value.format(dtf)); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + private final void _serializeAsArrayContents(LocalTime value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getHour()); + g.writeNumber(value.getMinute()); + int secs = value.getSecond(); + int nanos = value.getNano(); + if ((secs > 0) || (nanos > 0)) + { + g.writeNumber(secs); + if (nanos > 0) { + if (useNanoseconds(ctxt)) { + g.writeNumber(nanos); + } else { + g.writeNumber(value.get(ChronoField.MILLI_OF_SECOND)); + } + } + } + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + return useTimestamp(ctxt) ? JsonToken.START_ARRAY : JsonToken.VALUE_STRING; + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean useNanoseconds) { + return new LocalTimeSerializer(this, _formatter, + _useTimestamp, useNanoseconds); + } + + // as per [modules-java8#105] + @Override + public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + if (useTimestamp(visitor.getContext())) { + _acceptTimestampVisitor(visitor, typeHint); + } else { + JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint); + if (v2 != null) { + v2.format(JsonValueFormat.TIME); + } + } + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/MonthDaySerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/MonthDaySerializer.java new file mode 100644 index 0000000000..449bf155c1 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/MonthDaySerializer.java @@ -0,0 +1,104 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.MonthDay; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; + +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; + +/** + * Serializer for Java 8 temporal {@link MonthDay}s. + *

+ * NOTE: unlike many other date/time type serializers, this serializer will only + * use Array notation if explicitly instructed to do so with JsonFormat + * (either directly or through per-type defaults) and NOT with global defaults. + * + * @since 2.7.1 + */ +public class MonthDaySerializer extends JSR310FormattedSerializerBase +{ + public static final MonthDaySerializer INSTANCE = new MonthDaySerializer(); + + protected MonthDaySerializer() { // was private before 2.12 + this(null); + } + + public MonthDaySerializer(DateTimeFormatter formatter) { + super(MonthDay.class, formatter); + } + + private MonthDaySerializer(MonthDaySerializer base, DateTimeFormatter dtf, Boolean useTimestamp) { + super(base, dtf, useTimestamp, null, null); + } + + @Override + protected MonthDaySerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new MonthDaySerializer(this, dtf, useTimestamp); + } + + @Override + public void serialize(MonthDay value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (_useTimestampExplicitOnly(ctxt)) { + g.writeStartArray(); + _serializeAsArrayContents(value, g, ctxt); + g.writeEndArray(); + } else { + g.writeString((_formatter == null) ? value.toString() : value.format(_formatter)); + } + } + + @Override + public void serializeWithType(MonthDay value, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + if ((typeIdDef != null) + && typeIdDef.valueShape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else { + g.writeString((_formatter == null) ? value.toString() : value.format(_formatter)); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + protected void _serializeAsArrayContents(MonthDay value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getMonthValue()); + g.writeNumber(value.getDayOfMonth()); + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + return _useTimestampExplicitOnly(ctxt) ? JsonToken.START_ARRAY : JsonToken.VALUE_STRING; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerializer.java new file mode 100644 index 0000000000..e410f570fb --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerializer.java @@ -0,0 +1,43 @@ +package tools.jackson.databind.datetime.ser; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; + +public class OffsetDateTimeSerializer extends InstantSerializerBase +{ + public static final OffsetDateTimeSerializer INSTANCE = new OffsetDateTimeSerializer(); + + protected OffsetDateTimeSerializer() { + super(OffsetDateTime.class, dt -> dt.toInstant().toEpochMilli(), + OffsetDateTime::toEpochSecond, OffsetDateTime::getNano, + DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + protected OffsetDateTimeSerializer(OffsetDateTimeSerializer base, + Boolean useTimestamp, DateTimeFormatter formatter, + JsonFormat.Shape shape) { + this(base, formatter, useTimestamp, base._useNanoseconds, shape); + } + + protected OffsetDateTimeSerializer(OffsetDateTimeSerializer base, + DateTimeFormatter formatter, + Boolean useTimestamp, Boolean useNanoseconds, + JsonFormat.Shape shape) { + super(base, formatter, useTimestamp, useNanoseconds, shape); + } + + @Override + protected JSR310FormattedSerializerBase withFormat(DateTimeFormatter formatter, + Boolean useTimestamp, + JsonFormat.Shape shape) + { + return new OffsetDateTimeSerializer(this, useTimestamp, formatter, shape); + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean writeNanoseconds) { + return new OffsetDateTimeSerializer(this, _formatter, + _useTimestamp, writeNanoseconds, _shape); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/OffsetTimeSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/OffsetTimeSerializer.java new file mode 100644 index 0000000000..d6bd19ab7c --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/OffsetTimeSerializer.java @@ -0,0 +1,122 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.OffsetTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; + +/** + * Serializer for Java 8 temporal {@link OffsetTime}s. + * + * @author Nick Williams + */ +public class OffsetTimeSerializer extends JSR310FormattedSerializerBase +{ + public static final OffsetTimeSerializer INSTANCE = new OffsetTimeSerializer(); + + protected OffsetTimeSerializer() { + super(OffsetTime.class); + } + + protected OffsetTimeSerializer(OffsetTimeSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp) { + this(base, dtf, useTimestamp, null); + } + + protected OffsetTimeSerializer(OffsetTimeSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp, Boolean useNanoseconds) { + super(base, dtf, useTimestamp, useNanoseconds, null); + } + + @Override + protected OffsetTimeSerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new OffsetTimeSerializer(this, dtf, useTimestamp); + } + + @Override + public void serialize(OffsetTime time, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + g.writeStartArray(); + _serializeAsArrayContents(time, g, ctxt); + g.writeEndArray(); + } else { + String str = (_formatter == null) ? time.toString() : time.format(_formatter); + g.writeString(str); + } + } + + @Override + public void serializeWithType(OffsetTime value, JsonGenerator g, SerializationContext ctxt, + TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + if ((typeIdDef != null) + && typeIdDef.valueShape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else { + String str = (_formatter == null) ? value.toString() : value.format(_formatter); + g.writeString(str); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + private final void _serializeAsArrayContents(OffsetTime value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getHour()); + g.writeNumber(value.getMinute()); + final int secs = value.getSecond(); + final int nanos = value.getNano(); + if ((secs > 0) || (nanos > 0)) { + g.writeNumber(secs); + if (nanos > 0) { + if(useNanoseconds(ctxt)) { + g.writeNumber(nanos); + } else { + g.writeNumber(value.get(ChronoField.MILLI_OF_SECOND)); + } + } + } + g.writeString(value.getOffset().toString()); + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + return useTimestamp(ctxt) ? JsonToken.START_ARRAY : JsonToken.VALUE_STRING; + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, Boolean writeNanoseconds) { + return new OffsetTimeSerializer(this, _formatter, _useTimestamp, writeNanoseconds); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerializer.java new file mode 100644 index 0000000000..a3beabd460 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerializer.java @@ -0,0 +1,33 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Month; + +import tools.jackson.core.JsonGenerator; + +import tools.jackson.databind.*; + +/** + * @since 2.17 + */ +public class OneBasedMonthSerializer extends ValueSerializer { + private final ValueSerializer _defaultSerializer; + + @SuppressWarnings("unchecked") + public OneBasedMonthSerializer(ValueSerializer defaultSerializer) + { + _defaultSerializer = (ValueSerializer) defaultSerializer; + } + + @Override + public void serialize(Month value, JsonGenerator gen, SerializationContext ctxt) + { + // 15-Jan-2024, tatu: [modules-java8#274] This is not really sufficient + // (see `jackson-databind` `EnumSerializer` for full logic), but has to + // do for now. May need to add `@JsonFormat.shape` handling in future. + if (ctxt.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)) { + gen.writeNumber(value.ordinal() + 1); + return; + } + _defaultSerializer.serialize(value, gen, ctxt); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/YearMonthSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/YearMonthSerializer.java new file mode 100644 index 0000000000..56a453c406 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/YearMonthSerializer.java @@ -0,0 +1,122 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; + +import tools.jackson.databind.JavaType; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor; +import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; +import tools.jackson.databind.jsontype.TypeSerializer; + +/** + * Serializer for Java 8 temporal {@link YearMonth}s. + * + * @author Nick Williams + * @since 2.2 + */ +public class YearMonthSerializer extends JSR310FormattedSerializerBase +{ + public static final YearMonthSerializer INSTANCE = new YearMonthSerializer(); + + protected YearMonthSerializer() { // was private before 2.12 + this(null); + } + + public YearMonthSerializer(DateTimeFormatter formatter) { + super(YearMonth.class, formatter); + } + + private YearMonthSerializer(YearMonthSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp) { + super(base, dtf, useTimestamp, null, null); + } + + @Override + protected YearMonthSerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new YearMonthSerializer(this, dtf, useTimestamp); + } + + @Override + public void serialize(YearMonth value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + g.writeStartArray(); + _serializeAsArrayContents(value, g, ctxt); + g.writeEndArray(); + return; + } + g.writeString((_formatter == null) ? value.toString() : value.format(_formatter)); + } + + @Override + public void serializeWithType(YearMonth value, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) + throws JacksonException + { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, serializationShape(ctxt))); + // need to write out to avoid double-writing array markers + if ((typeIdDef != null) + && typeIdDef.valueShape == JsonToken.START_ARRAY) { + _serializeAsArrayContents(value, g, ctxt); + } else { + g.writeString((_formatter == null) ? value.toString() : value.format(_formatter)); + } + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + protected void _serializeAsArrayContents(YearMonth value, JsonGenerator g, + SerializationContext ctxt) + throws JacksonException + { + g.writeNumber(value.getYear()); + g.writeNumber(value.getMonthValue()); + } + + @Override + protected void _acceptTimestampVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + SerializationContext ctxt = visitor.getContext(); + boolean useTimestamp = (ctxt != null) && useTimestamp(ctxt); + if (useTimestamp) { + super._acceptTimestampVisitor(visitor, typeHint); + } else { + JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint); + if (v2 != null) { + v2.format(JsonValueFormat.DATE_TIME); + } + } + } + + @Override // since 2.9 + protected JsonToken serializationShape(SerializationContext ctxt) { + return useTimestamp(ctxt) ? JsonToken.START_ARRAY : JsonToken.VALUE_STRING; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/YearSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/YearSerializer.java new file mode 100644 index 0000000000..7e684ee268 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/YearSerializer.java @@ -0,0 +1,94 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Year; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; + +import tools.jackson.databind.JavaType; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; +import tools.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; + +/** + * Serializer for Java 8 temporal {@link Year}s. + * + * @author Nick Williams + */ +public class YearSerializer extends JSR310FormattedSerializerBase +{ + public static final YearSerializer INSTANCE = new YearSerializer(); + + protected YearSerializer() { + this(null); + } + + public YearSerializer(DateTimeFormatter formatter) { + super(Year.class, formatter); + } + + protected YearSerializer(YearSerializer base, DateTimeFormatter dtf, + Boolean useTimestamp) { + super(base, dtf, useTimestamp, null, null); + } + + @Override + protected YearSerializer withFormat(DateTimeFormatter dtf, + Boolean useTimestamp, JsonFormat.Shape shape) { + return new YearSerializer(this, dtf, useTimestamp); + } + + // Need to ensure Year still defaults to numeric ("timestamp") regardless + // of general global setting (since that defaults to textual in Jackson 3.x) + @Override + protected boolean useTimestampFromGlobalDefaults(SerializationContext ctxt) { + return true; + } + + @Override + public void serialize(Year year, JsonGenerator generator, SerializationContext ctxt) + throws JacksonException + { + if (useTimestamp(ctxt)) { + generator.writeNumber(year.getValue()); + } else { + String str = (_formatter == null) ? year.toString() : year.format(_formatter); + generator.writeString(str); + } + } + + // Override because we have String/Int, NOT String/Array + @Override + protected void _acceptTimestampVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) + { + JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint); + if (v2 != null) { + v2.numberType(JsonParser.NumberType.LONG); + } + } + + @Override // since 2.9 + protected JsonToken serializationShape(SerializationContext ctxt) { + return useTimestamp(ctxt) ? JsonToken.VALUE_NUMBER_INT : JsonToken.VALUE_STRING; + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/ZoneIdSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/ZoneIdSerializer.java new file mode 100644 index 0000000000..0daeadca89 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/ZoneIdSerializer.java @@ -0,0 +1,33 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.ZoneId; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.WritableTypeId; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; +import tools.jackson.databind.ser.std.ToStringSerializerBase; + +public class ZoneIdSerializer extends ToStringSerializerBase +{ + public ZoneIdSerializer() { super(ZoneId.class); } + + @Override + public void serializeWithType(Object value, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) + throws JacksonException + { + // Better ensure we don't use specific sub-classes: + WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, + typeSer.typeId(value, ZoneId.class, JsonToken.VALUE_STRING)); + serialize(value, g, ctxt); + typeSer.writeTypeSuffix(g, ctxt, typeIdDef); + } + + @Override + public String valueToString(Object value) { + return value.toString(); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerializer.java new file mode 100644 index 0000000000..235b77b29d --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerializer.java @@ -0,0 +1,107 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.SerializationFeature; + +public class ZonedDateTimeSerializer extends InstantSerializerBase { + public static final ZonedDateTimeSerializer INSTANCE = new ZonedDateTimeSerializer(); + + /** + * Flag for JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID + */ + protected final Boolean _writeZoneId; + + protected ZonedDateTimeSerializer() { + // ISO_ZONED_DATE_TIME is an extended version of ISO compliant format + // ISO_OFFSET_DATE_TIME with additional information :Zone Id + // (This is not part of the ISO-8601 standard) + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public ZonedDateTimeSerializer(DateTimeFormatter formatter) { + super(ZonedDateTime.class, dt -> dt.toInstant().toEpochMilli(), + ZonedDateTime::toEpochSecond, ZonedDateTime::getNano, + formatter); + _writeZoneId = null; + } + + protected ZonedDateTimeSerializer(ZonedDateTimeSerializer base, + DateTimeFormatter formatter, + Boolean useTimestamp, Boolean useNanoseconds, + Boolean writeZoneId, + JsonFormat.Shape shape) + { + super(base, formatter, useTimestamp, useNanoseconds, shape); + _writeZoneId = writeZoneId; + } + + @Override + protected JSR310FormattedSerializerBase withFormat(DateTimeFormatter formatter, + Boolean useTimestamp, + JsonFormat.Shape shape) + { + return new ZonedDateTimeSerializer(this, formatter, + useTimestamp, _useNanoseconds, _writeZoneId, + shape); + } + + @Override + protected JSR310FormattedSerializerBase withFeatures(Boolean writeZoneId, + Boolean useNanoseconds) + { + return new ZonedDateTimeSerializer(this, _formatter, + _useTimestamp, useNanoseconds, writeZoneId, _shape); + } + + @Override + public void serialize(ZonedDateTime value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + if (!useTimestamp(ctxt)) { + // [modules-java8#333]: `@JsonFormat` with pattern should override + // `SerializationFeature.WRITE_DATES_WITH_ZONE_ID` + if ((_formatter != null) && (_shape == JsonFormat.Shape.STRING)) { + ; // use default handling + } else if (shouldWriteWithZoneId(ctxt)) { + // write with zone + g.writeString(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(value)); + return; + } + } + super.serialize(value, g, ctxt); + } + + @Override + protected String formatValue(ZonedDateTime value, SerializationContext ctxt) { + String formatted = super.formatValue(value, ctxt); + // [modules-java8#333]: `@JsonFormat` with pattern should override + // `SerializationFeature.WRITE_DATES_WITH_ZONE_ID` + if (_formatter != null && _shape == JsonFormat.Shape.STRING) { + // Why not `if (shouldWriteWithZoneId(provider))` ? + if (Boolean.TRUE.equals(_writeZoneId)) { + formatted += "[" + value.getZone().getId() + "]"; + } + } + return formatted; + } + public boolean shouldWriteWithZoneId(SerializationContext ctxt) { + return (_writeZoneId != null) ? _writeZoneId : + ctxt.isEnabled(SerializationFeature.WRITE_DATES_WITH_ZONE_ID); + } + + @Override + protected JsonToken serializationShape(SerializationContext ctxt) { + if (!useTimestamp(ctxt) && shouldWriteWithZoneId(ctxt)) { + return JsonToken.VALUE_STRING; + } + return super.serializationShape(ctxt); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/ser/key/ZonedDateTimeKeySerializer.java b/src/main/java/tools/jackson/databind/datetime/ser/key/ZonedDateTimeKeySerializer.java new file mode 100644 index 0000000000..7ed703ea5c --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/ser/key/ZonedDateTimeKeySerializer.java @@ -0,0 +1,49 @@ +package tools.jackson.databind.datetime.ser.key; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; + +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.datetime.util.DecimalUtils; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.SerializationFeature; + +public class ZonedDateTimeKeySerializer extends ValueSerializer { + + public static final ZonedDateTimeKeySerializer INSTANCE = new ZonedDateTimeKeySerializer(); + + private ZonedDateTimeKeySerializer() { + // singleton + } + + @Override + public void serialize(ZonedDateTime value, JsonGenerator g, SerializationContext ctxt) + throws JacksonException + { + /* [modules-java8#127]: Serialization of timezone data is disabled by default, but can be + * turned on by enabling `SerializationFeature.WRITE_DATES_WITH_ZONE_ID` + */ + if (ctxt.isEnabled(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)) { + g.writeName(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(value)); + } else if (useTimestamps(ctxt)) { + if (useNanos(ctxt)) { + g.writeName(DecimalUtils.toBigDecimal(value.toEpochSecond(), value.getNano()).toString()); + } else { + g.writeName(String.valueOf(value.toInstant().toEpochMilli())); + } + } else { + g.writeName(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value)); + } + } + + private static boolean useNanos(SerializationContext ctxt) { + return ctxt.isEnabled(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); + } + + private static boolean useTimestamps(SerializationContext ctxt) { + return ctxt.isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/util/DecimalUtils.java b/src/main/java/tools/jackson/databind/datetime/util/DecimalUtils.java new file mode 100644 index 0000000000..5a05b8749d --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/util/DecimalUtils.java @@ -0,0 +1,136 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.util; + +import tools.jackson.core.io.NumberInput; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.function.BiFunction; + +/** + * Utilities to aid in the translation of decimal types to/from multiple parts. + * + * @author Nick Williams + */ +public final class DecimalUtils +{ + private DecimalUtils() { } + + public static String toDecimal(long seconds, int nanoseconds) + { + StringBuilder sb = new StringBuilder(20) + .append(seconds) + .append('.'); + // 14-Mar-2016, tatu: Although we do not yet (with 2.7) trim trailing zeroes, + // for general case, + if (nanoseconds == 0L) { + // !!! TODO: 14-Mar-2016, tatu: as per [datatype-jsr310], should trim + // trailing zeroes + if (seconds == 0L) { + return "0.0"; + } + +// sb.append('0'); + sb.append("000000000"); + } else { + StringBuilder nanoSB = new StringBuilder(9); + nanoSB.append(nanoseconds); + // May need to both prepend leading nanos (if value less than 0.1) + final int nanosLen = nanoSB.length(); + int prepZeroes = 9 - nanosLen; + while (prepZeroes > 0) { + --prepZeroes; + sb.append('0'); + } + + // !!! TODO: 14-Mar-2016, tatu: as per [datatype-jsr310], should trim + // trailing zeroes + /* + // AND possibly trim trailing ones + int i = nanosLen; + while ((i > 1) && nanoSB.charAt(i-1) == '0') { + --i; + } + if (i < nanosLen) { + nanoSB.setLength(i); + } + */ + sb.append(nanoSB); + } + return sb.toString(); + } + + /** + * Factory method for constructing {@link BigDecimal} out of second, nano-second + * components. + */ + public static BigDecimal toBigDecimal(long seconds, int nanoseconds) + { + if (nanoseconds == 0L) { + // 14-Mar-2015, tatu: Let's retain one zero to avoid interpretation + // as integral number + if (seconds == 0L) { // except for "0.0" where it can not be done without scientific notation + return BigDecimal.ZERO.setScale(1); + } + return BigDecimal.valueOf(seconds).setScale(9); + } + return NumberInput.parseBigDecimal(toDecimal(seconds, nanoseconds), false); + } + + /** + * Extracts the seconds and nanoseconds component of {@code seconds} as {@code long} and {@code int} + * values, passing them to the given converter. The implementation avoids latency issues present + * on some JRE releases. + * + * @since 2.19 + */ + public static T extractSecondsAndNanos(BigDecimal seconds, + BiFunction convert, boolean negativeAdjustment) { + // Complexity is here to workaround unbounded latency in some BigDecimal operations. + // https://github.com/FasterXML/jackson-databind/issues/2141 + long secondsOnly; + int nanosOnly; + + BigDecimal nanoseconds = seconds.scaleByPowerOfTen(9); + if (nanoseconds.precision() - nanoseconds.scale() <= 0) { + // There are no non-zero digits to the left of the decimal point. + // This protects against very negative exponents. + secondsOnly = nanosOnly = 0; + } + else if (seconds.scale() < -63) { + // There would be no low-order bits once we chop to a long. + // This protects against very positive exponents. + secondsOnly = nanosOnly = 0; + } + else { + // Now we know that seconds has reasonable scale, we can safely chop it apart. + secondsOnly = seconds.longValue(); + nanosOnly = nanoseconds.subtract(BigDecimal.valueOf(secondsOnly).scaleByPowerOfTen(9)).intValue(); + + if (secondsOnly < 0 && secondsOnly > Instant.MIN.getEpochSecond()) { + // [modules-java8#337] since 2.19, not always we need to adjust nanos + if (negativeAdjustment) { + // Issue #69 and Issue #120: avoid sending a negative adjustment to the Instant constructor, we want this as the actual nanos + nanosOnly = Math.abs(nanosOnly); + } + } + } + + return convert.apply(secondsOnly, nanosOnly); + } +} diff --git a/src/main/java/tools/jackson/databind/datetime/util/DurationUnitConverter.java b/src/main/java/tools/jackson/databind/datetime/util/DurationUnitConverter.java new file mode 100644 index 0000000000..6a77806e23 --- /dev/null +++ b/src/main/java/tools/jackson/databind/datetime/util/DurationUnitConverter.java @@ -0,0 +1,82 @@ +package tools.jackson.databind.datetime.util; + +import static tools.jackson.databind.datetime.util.DurationUnitConverter.DurationSerialization.deserializer; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Handles the conversion of the duration based on the API of {@link Duration} for a restricted set of {@link ChronoUnit}. + * Only the units considered as accurate are supported in this converter since are the only ones capable of handling + * deserialization in a precise manner (see {@link ChronoUnit#isDurationEstimated}). + * + * @since 2.12 + */ +public class DurationUnitConverter { + + protected static class DurationSerialization { + final Function serializer; + final Function deserializer; + + DurationSerialization( + Function serializer, + Function deserializer) { + this.serializer = serializer; + this.deserializer = deserializer; + } + + static Function deserializer(TemporalUnit unit) { + return v -> Duration.of(v, unit); + } + } + + private final static Map UNITS; + + static { + Map units = new LinkedHashMap<>(); + units.put(ChronoUnit.NANOS.name(), new DurationSerialization(Duration::toNanos, deserializer(ChronoUnit.NANOS))); + units.put(ChronoUnit.MICROS.name(), new DurationSerialization(d -> d.toNanos() / 1000, deserializer(ChronoUnit.MICROS))); + units.put(ChronoUnit.MILLIS.name(), new DurationSerialization(Duration::toMillis, deserializer(ChronoUnit.MILLIS))); + units.put(ChronoUnit.SECONDS.name(), new DurationSerialization(Duration::getSeconds, deserializer(ChronoUnit.SECONDS))); + units.put(ChronoUnit.MINUTES.name(), new DurationSerialization(Duration::toMinutes, deserializer(ChronoUnit.MINUTES))); + units.put(ChronoUnit.HOURS.name(), new DurationSerialization(Duration::toHours, deserializer(ChronoUnit.HOURS))); + units.put(ChronoUnit.HALF_DAYS.name(), new DurationSerialization(d -> d.toHours() / 12, deserializer(ChronoUnit.HALF_DAYS))); + units.put(ChronoUnit.DAYS.name(), new DurationSerialization(Duration::toDays, deserializer(ChronoUnit.DAYS))); + UNITS = units; + } + + + final DurationSerialization serialization; + + DurationUnitConverter(DurationSerialization serialization) { + this.serialization = serialization; + } + + public Duration convert(long value) { + return serialization.deserializer.apply(value); + } + + public long convert(Duration duration) { + return serialization.serializer.apply(duration); + } + + /** + * @return Description of all allowed valued as a sequence of + * double-quoted values separated by comma + */ + public static String descForAllowed() { + return "\"" + UNITS.keySet().stream() + .collect(Collectors.joining("\", \"")) + + "\""; + } + + public static DurationUnitConverter from(String unit) { + DurationSerialization def = UNITS.get(unit); + return (def == null) ? null : new DurationUnitConverter(def); + } +} From 014145769b43e5906793fc21500f97b5469c4bf0 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 01:50:06 +0100 Subject: [PATCH 02/21] tests --- src/main/java/module-info.java | 4 + src/test/java/module-info.java | 8 + .../datetime/MockObjectConfiguration.java | 24 + .../databind/datetime/ModuleTestBase.java | 79 ++ .../databind/datetime/TestDecimalUtils.java | 153 +++ .../databind/datetime/TestFeatures.java | 48 + .../datetime/deser/DefaultTypingTest.java | 51 + .../datetime/deser/DurationDeser337Test.java | 49 + .../datetime/deser/DurationDeserTest.java | 585 +++++++++++ .../datetime/deser/InstantDeser291Test.java | 55 + .../datetime/deser/InstantDeserTest.java | 654 ++++++++++++ .../datetime/deser/LocalDateDeserTest.java | 601 +++++++++++ .../deser/LocalDateTimeDeserTest.java | 738 +++++++++++++ .../datetime/deser/LocalTimeDeserTest.java | 343 +++++++ .../datetime/deser/MonthDayDeserTest.java | 211 ++++ .../deser/OffsetDateTimeDeserTest.java | 824 +++++++++++++++ .../datetime/deser/OffsetTimeDeserTest.java | 350 +++++++ .../deser/OneBasedMonthDeserTest.java | 193 ++++ .../datetime/deser/PeriodDeserTest.java | 122 +++ .../datetime/deser/YearDeserTest.java | 252 +++++ .../datetime/deser/YearMonthDeserTest.java | 145 +++ .../datetime/deser/ZoneIdDeserTest.java | 142 +++ .../datetime/deser/ZoneOffsetDeserTest.java | 206 ++++ .../deser/ZonedDateTimeDeserTest.java | 311 ++++++ .../key/ZonedDateTimeKeyDeserializerTest.java | 59 ++ .../datetime/key/DurationAsKeyTest.java | 34 + .../datetime/key/InstantAsKeyTest.java | 50 + .../datetime/key/LocalDateAsKeyTest.java | 33 + .../datetime/key/LocalDateTimeAsKeyTest.java | 80 ++ .../datetime/key/LocalTimeAsKeyTest.java | 53 + .../datetime/key/MonthDayAsKeyTest.java | 36 + .../datetime/key/OffsetDateTimeAsKeyTest.java | 70 ++ .../datetime/key/OffsetTimeAsKeyTest.java | 70 ++ .../datetime/key/PeriodAsKeyTest.java | 54 + .../databind/datetime/key/YearAsKeyTest.java | 70 ++ .../datetime/key/YearMonthAsKeyTest.java | 34 + .../datetime/key/ZoneIdAsKeyTest.java | 62 ++ .../datetime/key/ZoneOffsetAsKeyTest.java | 48 + .../datetime/key/ZonedDateTimeAsKeyTest.java | 90 ++ .../datetime/misc/DateTimeExceptionTest.java | 24 + .../datetime/misc/DateTimeSchemasTest.java | 239 +++++ .../DeductionTypeSerialization296Test.java | 87 ++ .../datetime/misc/JDKSerializabilityTest.java | 46 + .../datetime/misc/UnsupportedTypesTest.java | 34 + .../datetime/ser/DurationSerTest.java | 322 ++++++ .../databind/datetime/ser/InstantSerTest.java | 189 ++++ .../datetime/ser/LocalDateSerTest.java | 191 ++++ .../datetime/ser/LocalDateTimeSerTest.java | 194 ++++ .../datetime/ser/LocalTimeSerTest.java | 199 ++++ .../datetime/ser/MonthDaySerTest.java | 60 ++ .../datetime/ser/OffsetDateTimeSerTest.java | 290 ++++++ .../datetime/ser/OffsetTimeSerTest.java | 171 ++++ .../datetime/ser/OneBasedMonthSerTest.java | 66 ++ .../databind/datetime/ser/PeriodSerTest.java | 56 + ...lDateSerializationWithCustomFormatter.java | 63 ++ ...eTimeSerializationWithCustomFormatter.java | 56 + ...lTimeSerializationWithCustomFormatter.java | 55 + ...MonthSerializationWithCustomFormatter.java | 56 + ...tYearSerializationWithCustomFormatter.java | 56 + ...eTimeSerializationWithCustomFormatter.java | 46 + .../datetime/ser/WriteNanosecondsTest.java | 119 +++ .../datetime/ser/WriteZoneIdTest.java | 94 ++ .../ser/YearMonthSerializationTest.java | 176 ++++ .../databind/datetime/ser/YearSerTest.java | 84 ++ .../databind/datetime/ser/ZoneIdSerTest.java | 57 ++ .../datetime/ser/ZoneOffsetSerTest.java | 68 ++ .../datetime/ser/ZonedDateTimeSerTest.java | 968 ++++++++++++++++++ ...ZonedDateTimeSerWithJsonFormat333Test.java | 43 + .../InstantDeserializerNegative359Test.java | 39 + .../tofix/InstantViaBigDecimal307Test.java | 47 + .../tofix/OffsetDateTimeDeser279Test.java | 50 + .../tofix/ZonedDateTimeIssue244Test.java | 72 ++ .../util/DurationUnitConverterTest.java | 53 + 73 files changed, 11361 insertions(+) create mode 100644 src/test/java/tools/jackson/databind/datetime/MockObjectConfiguration.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java create mode 100644 src/test/java/tools/jackson/databind/datetime/TestDecimalUtils.java create mode 100644 src/test/java/tools/jackson/databind/datetime/TestFeatures.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/DefaultTypingTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/DurationDeser337Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/DurationDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/InstantDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/LocalTimeDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/MonthDayDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/OffsetDateTimeDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/PeriodDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/YearDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/YearMonthDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/ZoneOffsetDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/DurationAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/InstantAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/LocalDateAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/LocalDateTimeAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/LocalTimeAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/MonthDayAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/OffsetDateTimeAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/OffsetTimeAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/PeriodAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/YearAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/YearMonthAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/ZoneIdAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/ZoneOffsetAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/key/ZonedDateTimeAsKeyTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/misc/DateTimeExceptionTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/misc/DateTimeSchemasTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/misc/DeductionTypeSerialization296Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/misc/JDKSerializabilityTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/misc/UnsupportedTypesTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/DurationSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/InstantSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/LocalDateSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/LocalTimeSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/MonthDaySerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/OffsetTimeSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/PeriodSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestYearSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/WriteNanosecondsTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/YearMonthSerializationTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/YearSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/ZoneIdSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/ZoneOffsetSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java create mode 100644 src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/tofix/InstantDeserializerNegative359Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/tofix/InstantViaBigDecimal307Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/tofix/OffsetDateTimeDeser279Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/tofix/ZonedDateTimeIssue244Test.java create mode 100644 src/test/java/tools/jackson/databind/datetime/util/DurationUnitConverterTest.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index ebc26ed986..227484b7be 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -25,6 +25,10 @@ exports tools.jackson.databind; exports tools.jackson.databind.annotation; exports tools.jackson.databind.cfg; + exports tools.jackson.databind.datetime; + exports tools.jackson.databind.datetime.deser; + exports tools.jackson.databind.datetime.ser; + exports tools.jackson.databind.datetime.util; exports tools.jackson.databind.deser; exports tools.jackson.databind.deser.bean; // Alas multiple types from this package are exported. Would diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 1126144cbb..050d4d6917 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -29,6 +29,14 @@ opens tools.jackson.databind; opens tools.jackson.databind.annotation; opens tools.jackson.databind.cfg; + opens tools.jackson.databind.datetime; + opens tools.jackson.databind.datetime.deser; + opens tools.jackson.databind.datetime.deser.key; + opens tools.jackson.databind.datetime.key; + opens tools.jackson.databind.datetime.misc; + opens tools.jackson.databind.datetime.ser; + opens tools.jackson.databind.datetime.tofix; + opens tools.jackson.databind.datetime.util; opens tools.jackson.databind.deser; opens tools.jackson.databind.deser.bean; opens tools.jackson.databind.deser.jackson; diff --git a/src/test/java/tools/jackson/databind/datetime/MockObjectConfiguration.java b/src/test/java/tools/jackson/databind/datetime/MockObjectConfiguration.java new file mode 100644 index 0000000000..1d402c18af --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/MockObjectConfiguration.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@class") +public interface MockObjectConfiguration +{ +} diff --git a/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java b/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java new file mode 100644 index 0000000000..41506ef54a --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java @@ -0,0 +1,79 @@ +package tools.jackson.databind.datetime; + +import java.time.ZoneId; +import java.util.*; + +import tools.jackson.core.json.JsonWriteFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.cfg.MapperBuilder; +import tools.jackson.databind.json.JsonMapper; + +public class ModuleTestBase +{ + protected static final ZoneId UTC = ZoneId.of("UTC"); + + protected static final ZoneId Z_CHICAGO = ZoneId.of("America/Chicago"); + protected static final ZoneId Z_BUDAPEST = ZoneId.of("Europe/Budapest"); + + // 14-Mar-2016, tatu: Serialization of trailing zeroes may change [datatype-jsr310#67] + // Note, tho, that "0.0" itself is special case; need to avoid scientific notation: + final protected static String NO_NANOSECS_SER = "0.0"; + final protected static String NO_NANOSECS_SUFFIX = ".000000000"; + + protected static ObjectMapper newMapper() { + return newMapperBuilder().build(); + } + + protected static MapperBuilder newMapperBuilder() { + return JsonMapper.builder() + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) + .addModule(new JavaTimeModule()); + } + + protected static MapperBuilder newMapperBuilder(TimeZone tz) { + return JsonMapper.builder() + .defaultTimeZone(tz) + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) + .addModule(new JavaTimeModule()); + } + + protected static ObjectMapper newMapper(TimeZone tz) { + return newMapperBuilder(tz).build(); + } + + protected static JsonMapper.Builder mapperBuilder() { + return JsonMapper.builder() + .defaultLocale(Locale.ENGLISH) + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) + .addModule(new JavaTimeModule()); + } + + protected String q(String value) { + return "\"" + value + "\""; + } + + protected String a2q(String json) { + return json.replace("'", "\""); + } + + protected void verifyException(Throwable e, String... matches) + { + String msg = e.getMessage(); + String lmsg = (msg == null) ? "" : msg.toLowerCase(); + for (String match : matches) { + String lmatch = match.toLowerCase(); + if (lmsg.indexOf(lmatch) >= 0) { + return; + } + } + throw new Error("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\""); + } + + protected static Map asMap(T key, String value) { + return Collections.singletonMap(key, value); + } + + protected static String mapAsString(String key, String value) { + return String.format("{\"%s\":\"%s\"}", key, value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/TestDecimalUtils.java b/src/test/java/tools/jackson/databind/datetime/TestDecimalUtils.java new file mode 100644 index 0000000000..6f293ec0cf --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/TestDecimalUtils.java @@ -0,0 +1,153 @@ +package tools.jackson.databind.datetime; + +import java.math.BigDecimal; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import tools.jackson.databind.datetime.util.DecimalUtils; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestDecimalUtils extends ModuleTestBase +{ + @Test + public void testToDecimal01() + { + String decimal = DecimalUtils.toDecimal(0, 0); + assertEquals(NO_NANOSECS_SER, decimal, "The returned decimal is not correct."); + + decimal = DecimalUtils.toDecimal(15, 72); + assertEquals("15.000000072", decimal, "The returned decimal is not correct."); + + decimal = DecimalUtils.toDecimal(19827342231L, 192837465); + assertEquals("19827342231.192837465", decimal, "The returned decimal is not correct."); + + decimal = DecimalUtils.toDecimal(19827342231L, 0); + assertEquals("19827342231"+NO_NANOSECS_SUFFIX, decimal, + "The returned decimal is not correct."); + + decimal = DecimalUtils.toDecimal(19827342231L, 999888000); + assertEquals("19827342231.999888000", decimal, + "The returned decimal is not correct."); + + decimal = DecimalUtils.toDecimal(-22704862, 599000000); + assertEquals("-22704862.599000000", decimal, + "The returned decimal is not correct."); + } + + private void checkExtractNanos(long expectedSeconds, int expectedNanos, BigDecimal decimal) + { + long seconds = decimal.longValue(); + assertEquals(expectedSeconds, seconds, "The second part is not correct."); + } + + @Test + public void testExtractNanosecondDecimal01() + { + BigDecimal value = new BigDecimal("0"); + checkExtractNanos(0L, 0, value); + } + + @Test + public void testExtractNanosecondDecimal02() + { + BigDecimal value = new BigDecimal("15.000000072"); + checkExtractNanos(15L, 72, value); + } + + @Test + public void testExtractNanosecondDecimal03() + { + BigDecimal value = new BigDecimal("15.72"); + checkExtractNanos(15L, 720000000, value); + } + + @Test + public void testExtractNanosecondDecimal04() + { + BigDecimal value = new BigDecimal("19827342231.192837465"); + checkExtractNanos(19827342231L, 192837465, value); + } + + @Test + public void testExtractNanosecondDecimal05() + { + BigDecimal value = new BigDecimal("19827342231"); + checkExtractNanos(19827342231L, 0, value); + } + + @Test + public void testExtractNanosecondDecimal06() + { + BigDecimal value = new BigDecimal("19827342231.999999999"); + checkExtractNanos(19827342231L, 999999999, value); + } + + private void checkExtractSecondsAndNanos(long expectedSeconds, int expectedNanos, BigDecimal decimal) + { + DecimalUtils.extractSecondsAndNanos(decimal, (Long s, Integer ns) -> { + assertEquals(expectedSeconds, s.longValue(), "The second part is not correct."); + assertEquals(expectedNanos, ns.intValue(), "The nanosecond part is not correct."); + return null; + }, true); + } + + @Test + public void testExtractSecondsAndNanos01() + { + BigDecimal value = new BigDecimal("0"); + checkExtractSecondsAndNanos(0L, 0, value); + } + + @Test + public void testExtractSecondsAndNanos02() + { + BigDecimal value = new BigDecimal("15.000000072"); + checkExtractSecondsAndNanos(15L, 72, value); + } + + @Test + public void testExtractSecondsAndNanos03() + { + BigDecimal value = new BigDecimal("15.72"); + checkExtractSecondsAndNanos(15L, 720000000, value); + } + + @Test + public void testExtractSecondsAndNanos04() + { + BigDecimal value = new BigDecimal("19827342231.192837465"); + checkExtractSecondsAndNanos(19827342231L, 192837465, value); + } + + @Test + public void testExtractSecondsAndNanos05() + { + BigDecimal value = new BigDecimal("19827342231"); + checkExtractSecondsAndNanos(19827342231L, 0, value); + } + + @Test + public void testExtractSecondsAndNanos06() + { + BigDecimal value = new BigDecimal("19827342231.999999999"); + checkExtractSecondsAndNanos(19827342231L, 999999999, value); + } + + @Test + public void testExtractSecondsAndNanosFromNegativeBigDecimal() + { + BigDecimal value = new BigDecimal("-22704862.599000000"); + checkExtractSecondsAndNanos(-22704862L, 599000000, value); + } + + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testExtractSecondsAndNanos07() + { + BigDecimal value = new BigDecimal("1e10000000"); + checkExtractSecondsAndNanos(0L, 0, value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/TestFeatures.java b/src/test/java/tools/jackson/databind/datetime/TestFeatures.java new file mode 100644 index 0000000000..ccb7d94af4 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/TestFeatures.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.SerializationFeature; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestFeatures +{ + @Test + public void testWriteDateTimestampsAsNanosecondsSettingEnabledByDefault() + { + assertTrue(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS.enabledByDefault(), + "Write date timestamps as nanoseconds setting should be enabled by default."); + } + + @Test + public void testReadDateTimestampsAsNanosecondsSettingEnabledByDefault() + { + assertTrue(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS.enabledByDefault(), + "Read date timestamps as nanoseconds setting should be enabled by default."); + } + + @Test + public void testAdjustDatesToContextTimeZoneSettingEnabledByDefault() + { + assertTrue(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE.enabledByDefault(), + "Adjust dates to context time zone setting should be enabled by default."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/DefaultTypingTest.java b/src/test/java/tools/jackson/databind/datetime/deser/DefaultTypingTest.java new file mode 100644 index 0000000000..7775f9d107 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/DefaultTypingTest.java @@ -0,0 +1,51 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.ZoneId; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.DatabindContext; +import tools.jackson.databind.JavaType; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.jsontype.PolymorphicTypeValidator; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DefaultTypingTest extends ModuleTestBase +{ + static class NoCheckSubTypeValidator + extends PolymorphicTypeValidator.Base + { + private static final long serialVersionUID = 1L; + + @Override + public Validity validateBaseType(DatabindContext ctxt, JavaType baseType) { + return Validity.ALLOWED; + } + } + + private final ObjectMapper TYPING_MAPPER = newMapperBuilder() + .activateDefaultTyping(new NoCheckSubTypeValidator()) + .build(); + + // for [datatype-jsr310#24] + @Test + public void testZoneIdAsIs() throws Exception + { + ZoneId exp = ZoneId.of("America/Chicago"); + String json = TYPING_MAPPER.writeValueAsString(exp); + ZoneId act = TYPING_MAPPER.readValue(json, ZoneId.class); + assertEquals(exp, act); + } + + // This one WILL add type info, since `ZoneId` is abstract type: + @Test + public void testZoneWithForcedBaseType() throws Exception + { + ZoneId exp = ZoneId.of("America/Chicago"); + String json = TYPING_MAPPER.writerFor(ZoneId.class).writeValueAsString(exp); + ZoneId act = TYPING_MAPPER.readValue(json, ZoneId.class); + assertEquals(exp, act); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/DurationDeser337Test.java b/src/test/java/tools/jackson/databind/datetime/deser/DurationDeser337Test.java new file mode 100644 index 0000000000..e810c6ddeb --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/DurationDeser337Test.java @@ -0,0 +1,49 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.*; + +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DurationDeser337Test extends ModuleTestBase +{ + @Test + public void testWithDurationsAsTimestamps() throws Exception + { + final ObjectMapper MAPPER_DURATION_TIMESTAMPS = mapperBuilder() + .enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .build(); + + Duration duration = Duration.parse("PT-43.636S"); + + String ser = MAPPER_DURATION_TIMESTAMPS.writeValueAsString(duration); + + assertEquals("-43.636000000", ser); + + Duration deser = MAPPER_DURATION_TIMESTAMPS.readValue(ser, Duration.class); + + assertEquals(duration, deser); + assertEquals(deser.toString(), "PT-43.636S"); + } + + @Test + public void testWithoutDurationsAsTimestamps() throws Exception + { + ObjectMapper mapper = mapperBuilder() + .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .build(); + + Duration duration = Duration.parse("PT-43.636S"); + + String ser = mapper.writeValueAsString(duration); + assertEquals(q("PT-43.636S"), ser); + + Duration deser = mapper.readValue(ser, Duration.class); + assertEquals(duration, deser); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/DurationDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/DurationDeserTest.java new file mode 100644 index 0000000000..eb96a50798 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/DurationDeserTest.java @@ -0,0 +1,585 @@ +package tools.jackson.databind.datetime.deser; + +import java.math.BigInteger; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAmount; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.InvalidDefinitionException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DurationDeserTest extends ModuleTestBase +{ + private final ObjectReader READER = newMapper().readerFor(Duration.class); + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + final static class Wrapper { + public Duration value; + + public Wrapper() { } + public Wrapper(Duration v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public Duration value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(Duration v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public Duration value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(Duration v) { value = v; } + } + + + @Test + public void testDeserializationAsFloat01() throws Exception + { + Duration value = READER.with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("60.0"); + assertEquals(Duration.ofSeconds(60L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsFloat02() throws Exception + { + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("60.0"); + assertEquals(Duration.ofSeconds(60L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsFloat03() throws Exception + { + Duration value = READER.with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("13498.000008374"); + assertEquals(Duration.ofSeconds(13498L, 8374), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsFloat04() throws Exception + { + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("13498.000008374"); + assertEquals(Duration.ofSeconds(13498L, 8374), value, "The value is not correct."); + } + + /** + * Test the upper-bound of Duration. + */ + @Test + public void testDeserializationAsFloatEdgeCase01() throws Exception + { + String input = Long.MAX_VALUE + ".999999999"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(Long.MAX_VALUE, value.getSeconds()); + assertEquals(999999999, value.getNano()); + } + + /** + * Test the lower-bound of Duration. + */ + @Test + public void testDeserializationAsFloatEdgeCase02() throws Exception + { + String input = Long.MIN_VALUE + ".0"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(Long.MIN_VALUE, value.getSeconds()); + assertEquals(0, value.getNano()); + } + + @Test + public void testDeserializationAsFloatEdgeCase03() throws Exception + { + // Duration can't go this low + assertThrows(ArithmeticException.class, () -> { + READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.MIN_VALUE + ".1"); + }); + } + + /* + * DurationDeserializer currently uses BigDecimal.longValue() which has surprising behavior + * for numbers outside the range of Long. Numbers less than 1e64 will result in the lower 64 bits. + * Numbers at or above 1e64 will always result in zero. + */ + + @Test + public void testDeserializationAsFloatEdgeCase04() throws Exception + { + // Just beyond the upper-bound of Duration. + String input = new BigInteger(Long.toString(Long.MAX_VALUE)).add(BigInteger.ONE) + ".0"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(Long.MIN_VALUE, value.getSeconds()); // We've turned a positive number into negative duration! + } + + @Test + public void testDeserializationAsFloatEdgeCase05() throws Exception + { + // Just beyond the lower-bound of Duration. + String input = new BigInteger(Long.toString(Long.MIN_VALUE)).subtract(BigInteger.ONE) + ".0"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(Long.MAX_VALUE, value.getSeconds()); // We've turned a negative number into positive duration! + } + + @Test + public void testDeserializationAsFloatEdgeCase06() throws Exception + { + // Into the positive zone where everything becomes zero. + String input = "1e64"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + @Test + public void testDeserializationAsFloatEdgeCase07() throws Exception + { + // Into the negative zone where everything becomes zero. + String input = "-1e64"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + /** + * Numbers with very large exponents can take a long time, but still result in zero. + * https://github.com/FasterXML/jackson-databind/issues/2141 + */ + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase08() throws Exception + { + String input = "1e10000000"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase09() throws Exception + { + String input = "-1e10000000"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + /** + * Same for large negative exponents. + */ + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase10() throws Exception + { + String input = "1e-10000000"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase11() throws Exception + { + String input = "-1e-10000000"; + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(input); + assertEquals(0, value.getSeconds()); + } + + @Test + public void testDeserializationAsInt01() throws Exception + { + Duration value = READER.with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("60"); + assertEquals(Duration.ofSeconds(60L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt02() throws Exception + { + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("60000"); + assertEquals(Duration.ofSeconds(60L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt03() throws Exception + { + Duration value = READER.with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("13498"); + assertEquals(Duration.ofSeconds(13498L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt04() throws Exception + { + Duration value = READER.without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("13498000"); + assertEquals(Duration.ofSeconds(13498L, 0), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt05() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + WrapperWithReadTimestampsAsNanosEnabled expected = + new WrapperWithReadTimestampsAsNanosEnabled(Duration.ofSeconds(13498L, 0)); + WrapperWithReadTimestampsAsNanosEnabled actual = + reader.readValue(wrapperPayload(13498)); + assertEquals(expected.value, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt06() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + WrapperWithReadTimestampsAsNanosDisabled expected = + new WrapperWithReadTimestampsAsNanosDisabled(Duration.ofSeconds(13498L, 0)); + WrapperWithReadTimestampsAsNanosDisabled actual = + reader.readValue(wrapperPayload(13498000)); + assertEquals(expected.value, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsString01() throws Exception + { + Duration exp = Duration.ofSeconds(60L, 0); + Duration value = READER.readValue('"' + exp.toString() + '"'); + assertEquals(exp, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsString02() throws Exception + { + Duration exp = Duration.ofSeconds(13498L, 8374); + Duration value = READER.readValue('"' + exp.toString() + '"'); + assertEquals(exp, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsString03() throws Exception + { + assertNull(READER.readValue("\" \""), "The value should be null."); + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + Duration duration = Duration.ofSeconds(13498L, 8374); + + String prefix = "[\"" + Duration.class.getName() + "\","; + + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + TemporalAmount value = mapper.readerFor(TemporalAmount.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(prefix + "13498.000008374]"); + + assertTrue(value instanceof Duration, "The value should be a Duration."); + assertEquals(duration, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02() throws Exception + { + String prefix = "[\"" + Duration.class.getName() + "\","; + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + TemporalAmount value = mapper.readerFor(TemporalAmount.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(prefix + "13498]"); + assertTrue(value instanceof Duration, "The value should be a Duration."); + assertEquals(Duration.ofSeconds(13498L), value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + String prefix = "[\"" + Duration.class.getName() + "\","; + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + TemporalAmount value = mapper + .readerFor(TemporalAmount.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(prefix + "13498837]"); + assertTrue(value instanceof Duration, "The value should be a Duration."); + assertEquals(Duration.ofSeconds(13498L, 837000000), value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04() throws Exception + { + Duration duration = Duration.ofSeconds(13498L, 8374); + String prefix = "[\"" + Duration.class.getName() + "\","; + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + TemporalAmount value = mapper.readerFor(TemporalAmount.class) + .readValue(prefix + '"' + duration.toString() + "\"]"); + assertTrue(value instanceof Duration, "The value should be a Duration."); + assertEquals(duration, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsArrayDisabled() throws Exception { + Duration exp = Duration.ofSeconds(13498L, 8374); + try { + READER.readValue("[\"" + exp.toString() + "\"]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Duration` from Array value"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + try { + READER.readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Duration` from Array value"); + } + try { + READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Duration` from Array value"); + } + } + + @Test + public void testDeserializationAsArrayEnabled() throws Exception { + Duration exp = Duration.ofSeconds(13498L, 8374); + Duration value = newMapper().readerFor(Duration.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[\"" + exp.toString() + "\"]"); + assertEquals(exp, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + Duration value = newMapper().readerFor(Duration.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "duration"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsNullStr = null; + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + Duration actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + Duration actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "duration"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(Duration.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + final String dateValAsNullStr = null; + + // even with strict, null value should be deserialized without throwing an exception + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String dateValAsEmptyStr = ""; + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + /* + /********************************************************** + /* Tests for custom patterns (modules-java8#184) + /********************************************************** + */ + + @Test + public void shouldDeserializeInNanos_whenNanosUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("NANOS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + assertEquals(Duration.ofNanos(25), wrapper.value); + } + + @Test + public void shouldDeserializeInMicros_whenMicrosUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("MICROS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.of(25, ChronoUnit.MICROS), wrapper.value); + } + + @Test + public void shouldDeserializeInMillis_whenMillisUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("MILLIS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.ofMillis(25), wrapper.value); + } + + @Test + public void shouldDeserializeInSeconds_whenSecondsUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("SECONDS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.ofSeconds(25), wrapper.value); + } + + @Test + public void shouldDeserializeInMinutes_whenMinutesUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("MINUTES"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.ofMinutes(25), wrapper.value); + } + + @Test + public void shouldDeserializeInHours_whenHoursUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("HOURS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.ofHours(25), wrapper.value); + } + + @Test + public void shouldDeserializeInHalfDays_whenHalfDaysUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("HALF_DAYS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.of(25, ChronoUnit.HALF_DAYS), wrapper.value); + } + + @Test + public void shouldDeserializeInDays_whenDaysUnitAsPattern_andValueIsInteger() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("DAYS"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25)); + + assertEquals(Duration.ofDays(25), wrapper.value); + } + + @Test + public void shouldIgnoreUnitPattern_whenValueIsFloat() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("MINUTES"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue(wrapperPayload(25.5)); + + assertEquals(Duration.parse("PT25.5S"), wrapper.value); + } + + @Test + public void shouldIgnoreUnitPattern_whenValueIsString() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("MINUTES"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + Wrapper wrapper = reader.readValue("{\"value\":\"PT25S\"}"); + + assertEquals(Duration.parse("PT25S"), wrapper.value); + } + + @Test + public void shouldFailForInvalidPattern() throws Exception { + ObjectMapper mapper = _mapperForPatternOverride("Nanos"); + ObjectReader reader = mapper.readerFor(Wrapper.class); + + try { + /*Wrapper wrapper =*/ reader.readValue(wrapperPayload(25)); + fail("Should not allow invalid 'pattern'"); + } catch (InvalidDefinitionException e) { + verifyException(e, "Bad 'pattern' definition (\"Nanos\")"); + verifyException(e, "expected one of ["); + } + } + + private String wrapperPayload(Number number) { + return "{\"value\":" + number + "}"; + } + + private ObjectMapper _mapperForPatternOverride(String patternStr) { + return mapperBuilder() + .withConfigOverride(Duration.class, + o -> o.setFormat(JsonFormat.Value.forPattern(patternStr))) + .build(); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java new file mode 100644 index 0000000000..6c2458750f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java @@ -0,0 +1,55 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Instant; +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.json.JsonReadFeature; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// [modules-java8#291] InstantDeserializer fails to parse negative numeric timestamp strings for +// pre-1970 values. +public class InstantDeser291Test + extends ModuleTestBase +{ + private final JsonMapper MAPPER = JsonMapper.builder() + .defaultLocale(Locale.ENGLISH) + .addModule(new JavaTimeModule() + .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + .build(); + private final ObjectReader READER = MAPPER.readerFor(Instant.class); + + private static final Instant INSTANT_3_SEC_AFTER_EPOC = Instant.ofEpochSecond(3); + private static final Instant INSTANT_3_SEC_BEFORE_EPOC = Instant.ofEpochSecond(-3); + + private static final String STR_3_SEC = "\"3.000000000\""; + private static final String STR_POSITIVE_3 = "\"+3.000000000\""; + private static final String STR_NEGATIVE_3 = "\"-3.000000000\""; + + /** + * Baseline that always succeeds, even before resolution of issue 291 + * @throws Exception + */ + @Test + public void testNormalNumericalString() throws Exception { + assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER.readValue(STR_3_SEC)); + } + + @Test + public void testNegativeNumericalString() throws Exception { + assertEquals(INSTANT_3_SEC_BEFORE_EPOC, READER.readValue(STR_NEGATIVE_3)); + } + + @Test + public void testAllowedPlusSignNumericalString() throws Exception { + assertEquals(INSTANT_3_SEC_AFTER_EPOC, READER.readValue(STR_POSITIVE_3)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeserTest.java new file mode 100644 index 0000000000..d4d175e65a --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeserTest.java @@ -0,0 +1,654 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.datetime.util.DecimalUtils; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.SerializationFeature; + +import static org.junit.jupiter.api.Assertions.*; +import static tools.jackson.databind.datetime.deser.InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX; + +public class InstantDeserTest extends ModuleTestBase +{ + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; + private static final String CUSTOM_PATTERN = "yyyy-MM-dd HH:mm:ss"; + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + static class Wrapper { + @JsonFormat( + // 22-Jun-2015, tatu: I'll be damned if I understand why pattern does not + // work here... but it doesn't. Someone with better date-fu has to come + // and fix this; until then I will only verify that we can force textual + // representation here + //pattern="YYYY-mm-dd", + shape=JsonFormat.Shape.STRING + ) + public Instant value; + + public Wrapper() { } + public Wrapper(Instant v) { value = v; } + } + + static class WrapperWithCustomPattern { + @JsonFormat( + pattern = CUSTOM_PATTERN, + shape=JsonFormat.Shape.STRING, + timezone = "UTC" + ) + public Instant valueInUTC; + + public WrapperWithCustomPattern() { } + public WrapperWithCustomPattern(Instant v) { + valueInUTC = v; + } + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public Instant value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(Instant v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=JsonFormat.Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public Instant value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(Instant v) { value = v; } + } + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(Instant.class); + + /* + /********************************************************************** + /* Basic deserialization from floating point value (seconds with fractions) + /********************************************************************** + */ + + @Test + public void testDeserializationAsFloat01() throws Exception { + assertEquals(Instant.ofEpochSecond(0L), + READER.readValue("0.000000000")); + } + + @Test + public void testDeserializationAsFloat02() throws Exception { + assertEquals(Instant.ofEpochSecond(123456789L, 183917322), + READER.readValue("123456789.183917322")); + } + + @Test + public void testDeserializationAsFloat03() throws Exception + { + Instant date = Instant.now(); + Instant value = READER.readValue( + DecimalUtils.toDecimal(date.getEpochSecond(), date.getNano())); + assertEquals(date, value); + } + + /** + * Test the upper-bound of Instant. + */ + @Test + public void testDeserializationAsFloatEdgeCase01() throws Exception + { + String input = Instant.MAX.getEpochSecond() + ".999999999"; + Instant value = READER.readValue(input); + assertEquals(value, Instant.MAX); + assertEquals(Instant.MAX.getEpochSecond(), value.getEpochSecond()); + assertEquals(999999999, value.getNano()); + } + + /** + * Test the lower-bound of Instant. + */ + @Test + public void testDeserializationAsFloatEdgeCase02() throws Exception + { + String input = Instant.MIN.getEpochSecond() + ".0"; + Instant value = READER.readValue(input); + assertEquals(value, Instant.MIN); + assertEquals(Instant.MIN.getEpochSecond(), value.getEpochSecond()); + assertEquals(0, value.getNano()); + } + + @Test + public void testDeserializationAsFloatEdgeCase03() throws Exception + { + // Instant can't go this low + String input = Instant.MIN.getEpochSecond() + ".1"; + assertThrows(DateTimeException.class, () -> READER.readValue(input)); + } + + /* + * InstantDeserializer currently uses BigDecimal.longValue() which has surprising behavior + * for numbers outside the range of Long. Numbers less than 1e64 will result in the lower 64 bits. + * Numbers at or above 1e64 will always result in zero. + */ + @Test + public void testDeserializationAsFloatEdgeCase04() throws Exception + { + // 1ns beyond the upper-bound of Instant. + String input = (Instant.MAX.getEpochSecond() + 1) + ".0"; + assertThrows(DateTimeException.class, () -> READER.readValue(input)); + } + + @Test + public void testDeserializationAsFloatEdgeCase05() throws Exception + { + // 1ns beyond the lower-bound of Instant. + String input = (Instant.MIN.getEpochSecond() - 1) + ".0"; + assertThrows(DateTimeException.class, () -> READER.readValue(input)); + } + + @Test + public void testDeserializationAsFloatEdgeCase06() throws Exception + { + // Into the positive zone where everything becomes zero. + Instant value = READER.readValue("1e64"); + assertEquals(0, value.getEpochSecond()); + } + + @Test + public void testDeserializationAsFloatEdgeCase07() throws Exception + { + // Into the negative zone where everything becomes zero. + Instant value = READER.readValue("-1e64"); + assertEquals(0, value.getEpochSecond()); + } + + /** + * Numbers with very large exponents can take a long time, but still result in zero. + * https://github.com/FasterXML/jackson-databind/issues/2141 + */ + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase08() throws Exception + { + Instant value = READER.readValue("1e10000000"); + assertEquals(0, value.getEpochSecond()); + } + + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase09() throws Exception + { + Instant value = READER.readValue("-1e10000000"); + assertEquals(0, value.getEpochSecond()); + } + + /** + * Same for large negative exponents. + */ + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase10() throws Exception + { + Instant value = READER.readValue("1e-10000000"); + assertEquals(0, value.getEpochSecond()); + } + + @Timeout(value = 100, unit = TimeUnit.MILLISECONDS) + @Test + public void testDeserializationAsFloatEdgeCase11() throws Exception + { + Instant value = READER.readValue("-1e-10000000"); + assertEquals(0, value.getEpochSecond()); + } + + /* + /********************************************************************** + /* Basic deserialization from Integer (long) value, as nanos + /********************************************************************** + */ + + @Test + public void testDeserializationAsInt01Nanoseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + Instant value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt02Nanoseconds() throws Exception + { + final long ts = 123456789L; + Instant date = Instant.ofEpochSecond(ts); + Instant value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(String.valueOf(ts)); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt03Nanoseconds() throws Exception + { + Instant date = Instant.now(); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + + Instant value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.getEpochSecond())); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt04Nanoseconds() throws Exception + { + ObjectReader reader = MAPPER.readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + Instant date = Instant.now(); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + WrapperWithReadTimestampsAsNanosEnabled expected = + new WrapperWithReadTimestampsAsNanosEnabled(date); + WrapperWithReadTimestampsAsNanosEnabled actual = reader.readValue( + a2q("{'value':" + date.getEpochSecond() + "}")); + assertEquals(expected.value, actual.value); + } + + /* + /********************************************************************** + /* Basic deserialization from Integer (long) value, as milliseconds + /********************************************************************** + */ + + @Test + public void testDeserializationAsInt01Milliseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + Instant value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt02Milliseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 422000000); + Instant value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789422"); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt03Milliseconds() throws Exception + { + Instant date = Instant.now(); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + + Instant value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toEpochMilli())); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsInt04Milliseconds() throws Exception + { + ObjectReader reader = MAPPER.readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + Instant date = Instant.now(); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + WrapperWithReadTimestampsAsNanosDisabled expected = + new WrapperWithReadTimestampsAsNanosDisabled(date); + WrapperWithReadTimestampsAsNanosDisabled actual = reader.readValue( + a2q("{'value':" + date.toEpochMilli() + "}")); + assertEquals(expected.value, actual.value); + } + + /* + /********************************************************************** + /* Basic deserialization from String (ISO-8601 timestamps) + /********************************************************************** + */ + + @Test + public void testDeserializationAsString01() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + Instant value = READER.readValue('"' + FORMATTER.format(date) + '"'); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsString02() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + Instant value = READER.readValue('"' + FORMATTER.format(date) + '"'); + assertEquals(date, value); + } + + @Test + public void testDeserializationAsString03() throws Exception + { + Instant date = Instant.now(); + + Instant value = READER.readValue('"' + FORMATTER.format(date) + '"'); + assertEquals(date, value); + } + + /* + /********************************************************************** + /* Polymorphic deserialization, numeric timestamps + /********************************************************************** + */ + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + Instant.class.getName() + "\",123456789.183917322]", Temporal.class + ); + assertTrue(value instanceof Instant, "The value should be an Instant."); + assertEquals(date, value); + } + + @Test + public void testDeserializationWithTypeInfo02() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 0); + ObjectMapper m = newMapperBuilder() + .enable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + Instant.class.getName() + "\",123456789]", Temporal.class + ); + assertTrue(value instanceof Instant, "The value should be an Instant."); + assertEquals(date, value); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 422000000); + ObjectMapper m = newMapperBuilder() + .disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + Instant.class.getName() + "\",123456789422]", Temporal.class + ); + + assertTrue(value instanceof Instant, "The value should be an Instant."); + assertEquals(date, value); + } + + @Test + public void testDeserializationWithTypeInfo04() throws Exception + { + Instant date = Instant.now(); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + Instant.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class + ); + assertTrue(value instanceof Instant, "The value should be an Instant."); + assertEquals(date, value); + } + + /* + /********************************************************************** + /* Deserialization with custom pattern overrides (for String values) + /********************************************************************** + */ + + @Test + public void testCustomPatternWithAnnotations01() throws Exception + { + final Wrapper input = new Wrapper(Instant.ofEpochMilli(0)); + String json = MAPPER.writeValueAsString(input); + assertEquals(a2q("{'value':'1970-01-01T00:00:00Z'}"), json); + + Wrapper result = MAPPER.readValue(json, Wrapper.class); + assertEquals(input.value, result.value); + } + + // [datatype-jsr310#69] + @Test + public void testCustomPatternWithAnnotations02() throws Exception + { + //Test date is pushed one year after start of the epoch just to avoid possible issues with UTC-X TZs which could + //push the instant before tha start of the epoch + final Instant instant = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneOffset.UTC).plusYears(1).toInstant(); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(CUSTOM_PATTERN); + final String valueInUTC = formatter.withZone(ZoneOffset.UTC).format(instant); + + final WrapperWithCustomPattern input = new WrapperWithCustomPattern(instant); + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains(a2q("'valueInUTC':'" + valueInUTC + "'")), + "Instant in UTC timezone was not serialized as expected."); + + WrapperWithCustomPattern result = MAPPER.readValue(json, WrapperWithCustomPattern.class); + assertEquals(input.valueInUTC, result.valueInUTC, + "Instant in UTC timezone was not deserialized as expected."); + } + + /* + /********************************************************************** + /* Deserialization, timezone overrides + /********************************************************************** + */ + + // [jackson-modules-java8#18] + @Test + public void testDeserializationFromStringWithZeroZoneOffset01() throws Exception { + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+00:00"); + Instant result = READER.readValue(json); + assertEquals(date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset02() throws Exception { + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+0000"); + Instant result = READER.readValue(json); + assertEquals(date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset03() throws Exception { + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+00"); + Instant result = READER.readValue(json); + assertEquals(date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset04() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+00:30"); + Instant result = READER.readValue(json); + assertNotEquals(date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset05() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "+01:30"); + Instant result = READER.readValue(json); + assertNotEquals(date, result); + } + + @Test + public void testDeserializationFromStringWithZeroZoneOffset06() throws Exception { + assumeInstantCanParseOffsets(); + Instant date = Instant.now(); + String json = formatWithZeroZoneOffset(date, "-00:00"); + Instant result = READER.readValue(json); + assertEquals(date, result); + } + + private String formatWithZeroZoneOffset(Instant date, String offset){ + return '"' + FORMATTER.format(date).replaceFirst("Z$", offset) + '"'; + } + + private static void assumeInstantCanParseOffsets() { + // 27-Jan-2025, tatu: We are on JDK 17 so true always + // DateTimeFormatter.ISO_INSTANT didn't handle offsets until JDK 12+. + ; + } + + /* + /********************************************************************** + /* Deserialization, misc other + /********************************************************************** + */ + + // [datatype-jsr310#16] + @Test + public void testDeserializationFromStringAsNumber() throws Exception + { + // First, baseline test with floating-point numbers + Instant inst = Instant.now(); + String json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(inst); + Instant result = READER.readValue(json); + assertNotNull(result); + assertEquals(result, inst); + + // but then quoted as JSON String + result = READER.readValue(String.format("\"%s\"", json)); + assertNotNull(result); + assertEquals(result, inst); + } + + // [datatype-jsr310#79] + @Test + public void testRoundTripOfInstantAndJavaUtilDate() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .build(); + + Instant givenInstant = LocalDate.of(2016, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(); + String json = mapper.writeValueAsString(java.util.Date.from(givenInstant)); + Instant actual = mapper.readValue(json, Instant.class); + + assertEquals(givenInstant, actual); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "duration"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsNullStr = null; + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + Duration actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + Duration actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "instant"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(Instant.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + /* + /************************************************************************ + /* Tests for InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX + /************************************************************************ + */ + @Test + public void testISO8601ColonlessRegexFindsOffset() { + Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher("2000-01-01T12:00+0100"); + + assertTrue(matcher.find(), "Matcher finds +0100 as an colonless offset"); + assertEquals(matcher.group(), "+0100", "Matcher groups +0100 as an colonless offset"); + } + + @Test + public void testISO8601ColonlessRegexFindsOffsetWithTZ() { + Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher("2000-01-01T12:00+0100[Europe/Paris]"); + + assertTrue(matcher.find(), "Matcher finds +0100 as an colonless offset"); + assertEquals(matcher.group(), "+0100", "Matcher groups +0100 as an colonless offset"); + } + + @Test + public void testISO8601ColonlessRegexDoesNotAffectNegativeYears() { + Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher("-2000-01-01T12:00+01:00[Europe/Paris]"); + + assertFalse(matcher.find(), "Matcher does not find -2000 (years) as an offset without colon"); + } + + @Test + public void testISO8601ColonlessRegexDoesNotAffectNegativeYearsWithColonless() { + Matcher matcher = ISO8601_COLONLESS_OFFSET_REGEX.matcher("-2000-01-01T12:00+0100[Europe/Paris]"); + + assertTrue(matcher.find(), "Matcher finds +0100 as an colonless offset"); + assertEquals(matcher.group(), "+0100", "Matcher groups +0100 as an colonless offset"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java new file mode 100644 index 0000000000..9209cbfd69 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java @@ -0,0 +1,601 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.*; +import java.time.temporal.Temporal; +import java.util.Map; +import java.util.TimeZone; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import com.fasterxml.jackson.annotation.OptBoolean; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; + +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.*; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(LocalDate.class); + private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() + .addModule(new JavaTimeModule().enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) + .build() + .readerFor(LocalDate.class); + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + final static class Wrapper { + @JsonFormat(pattern="yyyy_MM_dd'T'HH:mmZ", + shape=JsonFormat.Shape.STRING) + public LocalDate value; + + 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; } + } + + static class StrictWrapperWithFormat { + @JsonFormat(pattern="yyyy-MM-dd", + lenient = OptBoolean.FALSE) + public LocalDate value; + + public StrictWrapperWithFormat() { } + public StrictWrapperWithFormat(LocalDate v) { value = v; } + } + + final static class StrictWrapperWithYearOfEra { + @JsonFormat(pattern="yyyy-MM-dd G", + lenient = OptBoolean.FALSE) + public LocalDate value; + + public StrictWrapperWithYearOfEra() { } + public StrictWrapperWithYearOfEra(LocalDate v) { value = v; } + } + + final static class StrictWrapperWithYearWithoutEra { + @JsonFormat(pattern="uuuu-MM-dd", + lenient = OptBoolean.FALSE) + public LocalDate value; + + public StrictWrapperWithYearWithoutEra() { } + public StrictWrapperWithYearWithoutEra(LocalDate v) { value = v; } + } + + /* + /********************************************************** + /* Deserialization from Int array representation + /********************************************************** + */ + + @Test + public void testDeserializationAsTimestamp01() + { + assertEquals(LocalDate.of(1986, Month.JANUARY, 17), + READER.readValue("[1986,1,17]")); + } + + @Test + public void testDeserializationAsTimestamp02() + { + assertEquals(LocalDate.of(2013, Month.AUGUST, 21), + READER.readValue("[2013,8,21]")); + } + + /* + /********************************************************** + /* Deserialization from String representation + /********************************************************** + */ + + @Test + public void testDeserializationAsString01() + { + assertEquals(LocalDate.of(2000, Month.JANUARY, 1), READER.readValue(q("2000-01-01"))); + + LocalDate date = LocalDate.of(1986, Month.JANUARY, 17); + assertEquals(date, READER.readValue('"' + date.toString() + '"')); + + date = LocalDate.of(2013, Month.AUGUST, 21); + assertEquals(date, READER.readValue('"' + date.toString() + '"')); + } + + @Test + public void testDeserializationAsString02() + { + LocalDateTime date = LocalDateTime.now(); + assertEquals(date.toLocalDate(), READER.readValue('"' + date.toString() + '"')); + } + + @Test + public void testLenientDeserializationAsString01() throws Exception + { + Instant instant = Instant.now(); + LocalDate value = READER.readValue(q(instant.toString())); + assertEquals(LocalDateTime.ofInstant(instant, ZoneOffset.UTC).toLocalDate(), value); + } + + @Test + public void testLenientDeserializationAsString02() throws Exception + { + ObjectReader reader = READER.with(TimeZone.getTimeZone(Z_BUDAPEST)); + Instant instant = Instant.now(); + LocalDate value = reader.readValue(q(instant.toString())); + assertEquals(LocalDateTime.ofInstant(instant, ZoneOffset.UTC).toLocalDate(), value); + } + + @Test + public void testLenientDeserializationAsString03() throws Exception + { + Instant instant = Instant.now(); + LocalDate value = READER_USING_TIME_ZONE.readValue(q(instant.toString())); + assertEquals(LocalDateTime.ofInstant(instant, ZoneOffset.UTC).toLocalDate(), value); + } + + @ParameterizedTest + @CsvSource({ + "Europe/Budapest, 2024-07-21T21:59:59Z, 2024-07-21", + "Europe/Budapest, 2024-07-21T22:00:00Z, 2024-07-22", + "America/Chicago, 2024-07-22T04:59:59Z, 2024-07-21", + "America/Chicago, 2024-07-22T05:00:00Z, 2024-07-22" + }) + public void testLenientDeserializationAsString04(TimeZone zone, String string, LocalDate expected) throws Exception + { + ObjectReader reader = READER_USING_TIME_ZONE.with(zone); + LocalDate value = reader.readValue(q(string)); + assertEquals(expected, value); + } + + @Test + public void testBadDeserializationAsString01() + { + try { + READER.readValue(q("notalocaldate")); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type"); + verifyException(e, "from String \""); + } + } + + @Test + public void testBadDeserializationAsString02() + { + try { + READER.readValue(q("2015-06-19TShouldNotParse")); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type"); + verifyException(e, "from String \""); + } + } + + @Test + public void testDeserializationWithTypeInfo01() + { + ObjectMapper mapper = mapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + LocalDate date = LocalDate.of(2005, Month.NOVEMBER, 5); + Temporal value = mapper.readValue( + "[\"" + LocalDate.class.getName() + "\",\"" + date.toString() + "\"]", Temporal.class + ); + assertEquals(date, value); + } + + /* + /********************************************************** + /* Deserialization from alternate representation: int (number + /* of days since Epoch) + /********************************************************** + */ + + // By default, lenient handling on so we can do this: + @Test + public void testLenientDeserializeFromInt() + { + assertEquals(LocalDate.of(1970, Month.JANUARY, 3), READER.readValue("2")); + + assertEquals(LocalDate.of(1970, Month.FEBRUARY, 10), READER.readValue("40")); + } + + // But with alternate setting, not so + @Test + public void testStricDeserializeFromInt() + { + ObjectMapper mapper = mapperBuilder() + .withConfigOverride(LocalDate.class, + c -> c.setFormat(JsonFormat.Value.forLeniency(false)) + ) + .build(); + try { + mapper.readValue("2", LocalDate.class); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize instance of"); + verifyException(e, "not allowed because 'strict' mode set for property or type"); + } + + // 17-Aug-2019, tatu: Should possibly test other mechanism too, but for now let's + // be content with just one... + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() + { + + String key = "date"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsNullStr = null; + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + LocalDate actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + LocalDate actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(actualDateFromNullStr, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + // ( expected = MismatchedInputException.class) + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "date"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(LocalDate.class, + c -> c.setFormat(JsonFormat.Value.forLeniency(false)) + ) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + final String dateValAsNullStr = null; + + // even with strict, null value should be deserialized without throwing an exception + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String dateValAsEmptyStr = ""; + // TODO: nothing stops us from writing an empty string, maybe there should be a check there too? + String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", dateValAsEmptyStr)); + // with strict, deserializing an empty string is not permitted + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + /* + /********************************************************** + /* Tests for alternate array handling + /********************************************************** + */ + + @Test + public void testDeserializationAsArrayDisabled() + { + try { + READER.readValue("[\"2000-01-01\"]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Unexpected token (VALUE_STRING) within Array"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() + { + // works even without the feature enabled + assertNull(READER.readValue("[]")); + } + + @Test + public void testDeserializationAsArrayEnabled() + { + LocalDate actual = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[\"2000-01-01\"]"); + assertEquals(LocalDate.of(2000, 1, 1), actual); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() + { + LocalDate value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + /* + /********************************************************** + /* Custom format + /********************************************************** + */ + + // for [datatype-jsr310#37] + @Test + public void testCustomFormat() + { + Wrapper w = MAPPER.readValue("{\"value\":\"2015_07_28T13:53+0300\"}", Wrapper.class); + LocalDate date = w.value; + assertEquals(28, date.getDayOfMonth()); + } + + /* + /********************************************************** + /* Strict Custom format + /********************************************************** + */ + + // for [modules-java8#148] + @Test + public void testStrictWithCustomFormat() + { + try { + /*StrictWrapperWithFormat w =*/ MAPPER.readValue( + "{\"value\":\"2019-11-31\"}", + StrictWrapperWithFormat.class); + fail("Should not pass"); + } catch (InvalidFormatException e) { + verifyException(e, "Cannot deserialize value of type `java.time.LocalDate` from String"); + verifyException(e, "\"2019-11-31\""); + } + } + + @Test + public void testStrictCustomFormatForInvalidFormat() 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\""); + } + } + + @Test + public void testStrictCustomFormatForInvalidFormatWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, () -> { + /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30\"}", StrictWrapperWithYearOfEra.class); + }); + } + + @Test + public void testStrictCustomFormatForInvalidDateWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, () -> { + /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31 AD\"}", StrictWrapperWithYearOfEra.class); + }); + } + + @Test + public void testStrictCustomFormatForValidDateWithEra() throws Exception + { + StrictWrapperWithYearOfEra w = MAPPER.readValue("{\"value\":\"2019-11-30 AD\"}", StrictWrapperWithYearOfEra.class); + + assertEquals(w.value, LocalDate.of(2019, 11, 30)); + } + + @Test + public void testStrictCustomFormatForInvalidFormatWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, () -> { + /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 AD\"}", StrictWrapperWithYearWithoutEra.class); + }); + } + + @Test + public void testStrictCustomFormatForInvalidDateWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, () -> { + /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31\"}", StrictWrapperWithYearWithoutEra.class); + }); + } + + @Test + public void testStrictCustomFormatForValidDateWithoutEra() throws Exception + { + StrictWrapperWithYearWithoutEra w = MAPPER.readValue("{\"value\":\"2019-11-30\"}", StrictWrapperWithYearWithoutEra.class); + + assertEquals(w.value, LocalDate.of(2019, 11, 30)); + } + + /* + /********************************************************************** + /* Case-insensitive tests + /********************************************************************** + */ + + @Test + public void testDeserializationCaseInsensitiveEnabledOnValue() + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, o -> o.setFormat(JsonFormat.Value + .forPattern("dd-MMM-yyyy") + .withFeature(Feature.ACCEPT_CASE_INSENSITIVE_VALUES)) + ) + .build(); + ObjectReader reader = mapper.readerFor(LocalDate.class); + String[] jsons = new String[] { q("01-Jan-2000"), q("01-JAN-2000"), + q("01-jan-2000")}; + for (String json : jsons) { + expectSuccess(reader, LocalDate.of(2000, Month.JANUARY, 1), json); + } + } + + @Test + public void testDeserializationCaseInsensitiveEnabled() + { + final ObjectMapper mapper = mapperBuilder() + .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES) + .withConfigOverride(LocalDate.class, o -> o.setFormat( + JsonFormat.Value.forPattern("dd-MMM-yyyy"))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDate.class); + String[] jsons = new String[] { q("01-Jan-2000"), q("01-JAN-2000"), + q("01-jan-2000")}; + for(String json : jsons) { + expectSuccess(reader, LocalDate.of(2000, Month.JANUARY, 1), json); + } + } + + @Test + public void testDeserializationCaseInsensitiveDisabled() + { + final ObjectMapper mapper = mapperBuilder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false) + .withConfigOverride(LocalDate.class, o -> o.setFormat( + JsonFormat.Value.forPattern("dd-MMM-yyyy"))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDate.class); + expectSuccess(reader, LocalDate.of(2000, Month.JANUARY, 1), q("01-Jan-2000")); + } + + @Test + public void testDeserializationCaseInsensitiveDisabled_InvalidDate() + { + final ObjectMapper mapper = mapperBuilder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false) + .withConfigOverride(LocalDate.class, o -> JsonFormat.Value.forPattern("dd-MMM-yyyy")) + .build(); + ObjectReader reader = mapper.readerFor(LocalDate.class); + String[] jsons = new String[] { q("01-JAN-2000"), q("01-jan-2000")}; + for(String json : jsons) { + try { + reader.readValue(a2q(json)); + fail("expected DateTimeParseException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.LocalDate` from String "); + } + } + } + + /* + /********************************************************************** + /* Tests for issue 58 - NUMBER_INT should be specified when deserializing + /* LocalDate as EpochDays + /********************************************************************** + */ + + @Test + public void testLenientDeserializeFromNumberInt() { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER_INT))) + .build(); + + assertEquals(LocalDate.of(1970, Month.MAY, 4), + mapper.readValue("123", LocalDate.class)); + } + + @Test + public void testStrictDeserializeFromNumberInt() + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + ShapeWrapper w = mapper.readValue("{\"date\":123}", ShapeWrapper.class); + LocalDate localDate = w.date; + + assertEquals(LocalDate.of(1970, Month.MAY, 4), localDate); + } + + @Test + public void testStrictDeserializeFromString() + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + try { + mapper.readValue("{\"value\":123}", Wrapper.class); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize instance of `java.time.LocalDate`"); + } + } + + /********************************************************************** + * + * coercion config test + * + /********************************************************************** + */ + + @Test + public void testDeserializeFromIntegerWithCoercionActionFail() { + ObjectMapper mapper = newMapperBuilder() + .withCoercionConfig(LocalDate.class, cfg -> + cfg.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + ).build(); + MismatchedInputException exception = assertThrows(MismatchedInputException.class, + () -> mapper.readValue("123", LocalDate.class)); + + assertTrue(exception.getMessage().contains("Cannot coerce Integer value (123) to `java.time.LocalDate`")); + } + + @Test + public void testDeserializeFromEmptyStringWithCoercionActionFail() { + ObjectMapper mapper = newMapperBuilder() + .withCoercionConfig(LocalDate.class, cfg -> + cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail) + ).build(); + + MismatchedInputException exception = assertThrows(MismatchedInputException.class, + () -> mapper.readValue(a2q("{'value':''}"), Wrapper.class)); + + assertTrue(exception.getMessage().contains("Cannot coerce empty String (\"\") to `java.time.LocalDate`")); + } + + /* + /********************************************************************** + /* Helper methods + /********************************************************************** + */ + + private void expectSuccess(ObjectReader reader, Object exp, String json) { + final LocalDate value = reader.readValue(a2q(json)); + assertNotNull(value); + assertEquals(exp, value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java new file mode 100644 index 0000000000..98a46d0ed0 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java @@ -0,0 +1,738 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.temporal.Temporal; +import java.util.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; +import com.fasterxml.jackson.annotation.OptBoolean; + +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.*; +import tools.jackson.databind.deser.DeserializationProblemHandler; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateTimeDeserTest + extends ModuleTestBase +{ + private final static ObjectMapper MAPPER = newMapper(); + private final static ObjectReader READER = MAPPER.readerFor(LocalDateTime.class); + + private final static ObjectMapper STRICT_MAPPER = mapperBuilder() + .withConfigOverride(LocalDateTime.class, + c -> c.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() + .addModule(new JavaTimeModule().enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) + .build() + .readerFor(LocalDateTime.class); + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + final static class StrictWrapper { + @JsonFormat(pattern="yyyy-MM-dd HH:mm", + lenient = OptBoolean.FALSE) + public LocalDateTime value; + + public StrictWrapper() { } + public StrictWrapper(LocalDateTime v) { value = v; } + } + + final static class StrictWrapperWithYearOfEra { + @JsonFormat(pattern="yyyy-MM-dd HH:mm G", + lenient = OptBoolean.FALSE) + public LocalDateTime value; + + public StrictWrapperWithYearOfEra() { } + public StrictWrapperWithYearOfEra(LocalDateTime v) { value = v; } + } + + final static class StrictWrapperWithYearWithoutEra { + @JsonFormat(pattern="uuuu-MM-dd HH:mm", + lenient = OptBoolean.FALSE) + public LocalDateTime value; + + public StrictWrapperWithYearWithoutEra() { } + public StrictWrapperWithYearWithoutEra(LocalDateTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public LocalDateTime value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(LocalDateTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public LocalDateTime value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(LocalDateTime v) { value = v; } + } + + /* + /********************************************************** + /* Tests for deserializing from int array + /********************************************************** + */ + + @Test + public void testDeserializationAsTimestamp01() + { + LocalDateTime value = READER.readValue("[1986,1,17,15,43]"); + LocalDateTime time = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp02() + { + LocalDateTime value = READER.readValue("[2013,8,21,9,22,57]"); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 57); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Nanoseconds() + { + ObjectReader r = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + LocalDateTime value = r.readValue("[2013,8,21,9,22,0,57]"); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Milliseconds() + { + ObjectReader r = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + LocalDateTime value = r.readValue("[2013,8,21,9,22,0,57]"); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57000000); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Nanoseconds() + { + ObjectReader r = MAPPER.readerFor(LocalDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + LocalDateTime value = r.readValue("[2005,11,5,22,31,5,829837]"); + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds01() + { + ObjectReader r = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + LocalDateTime value = r.readValue("[2005,11,5,22,31,5,829837]"); + + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds02() + { + ObjectReader r = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); + LocalDateTime value = r.readValue("[2005,11,5,22,31,5,829]"); + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829000000); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Nanoseconds() throws Exception + { + ObjectReader r = MAPPER.readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + WrapperWithReadTimestampsAsNanosEnabled actual = + r.readValue(a2q("{'value':[2013,8,21,9,22,0,57]}")); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); + assertEquals(time, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds01() throws Exception + { + ObjectReader r = MAPPER.readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + WrapperWithReadTimestampsAsNanosDisabled actual = + r.readValue(a2q("{'value':[2013,8,21,9,22,0,57]}")); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57000000); + assertEquals(time, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds02() throws Exception + { + ObjectReader r = MAPPER.readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + WrapperWithReadTimestampsAsNanosDisabled actual = + r.readValue(a2q("{'value':[2013,8,21,9,22,0,4257]}")); + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 4257); + assertEquals(time, actual.value, "The value is not correct."); + } + + /* + /********************************************************** + /* Tests for deserializing from textual representation + /********************************************************** + */ + + @Test + public void testDeserializationAsString01() + { + LocalDateTime exp = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43); + LocalDateTime value = READER.readValue(q(exp.toString())); + assertEquals(exp, value, "The value is not correct."); + + assertEquals(LocalDateTime.of(2000, Month.JANUARY, 1, 12, 0), + READER.readValue(q("2000-01-01T12:00")), + "The value is not correct."); + } + + @Test + public void testDeserializationAsString02() + { + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 57); + LocalDateTime value = MAPPER.readValue(q(time.toString()), LocalDateTime.class); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsString03() + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + LocalDateTime value = MAPPER.readValue(q(time.toString()), LocalDateTime.class); + assertEquals(time, value, "The value is not correct."); + } + + /* + /********************************************************** + /* Tests for deserializing from textual representation, + /* fail cases, leniency checking + /********************************************************** + */ + + // [modules-java#94]: "Z" offset MAY be allowed, requires leniency + @Test + public void testAllowZuluIfLenient() + { + final LocalDateTime EXP = LocalDateTime.of(2020, Month.OCTOBER, 22, 4, 16, 20, 504000000); + final String input = q("2020-10-22T04:16:20.504Z"); + final ObjectReader r = MAPPER.readerFor(LocalDateTime.class); + + // First, defaults: + assertEquals(EXP, r.readValue(input), "The value is not correct."); + + // but ensure that global timezone setting doesn't matter + LocalDateTime value = r.with(TimeZone.getTimeZone(Z_CHICAGO)) + .readValue(input); + assertEquals(EXP, value, "The value is not correct."); + + value = r.with(TimeZone.getTimeZone(Z_BUDAPEST)) + .readValue(input); + assertEquals(EXP, value, "The value is not correct."); + } + + @ParameterizedTest + @CsvSource({ + "UTC, 2020-10-22T04:16:20.504Z, 2020-10-22T04:16:20.504", + "Europe/Budapest, 2020-10-22T04:16:20.504Z, 2020-10-22T06:16:20.504", + "Europe/Budapest, 2020-10-25T00:16:20.504Z, 2020-10-25T02:16:20.504", + "Europe/Budapest, 2020-10-25T01:16:20.504Z, 2020-10-25T02:16:20.504", + "America/Chicago, 2020-10-22T04:16:20.504Z, 2020-10-21T23:16:20.504", + "America/Chicago, 2020-11-01T06:16:20.504Z, 2020-11-01T01:16:20.504", + "America/Chicago, 2020-11-01T07:16:20.504Z, 2020-11-01T01:16:20.504" + }) + public void testUseTimeZoneForZuluIfEnabled(TimeZone zone, String string, LocalDateTime expected) throws Exception + { + ObjectReader reader = READER_USING_TIME_ZONE.with(zone); + LocalDateTime value = reader.readValue(q(string)); + assertEquals(expected, value); + } + + // [modules-java#94]: "Z" offset not allowed if strict mode + @Test + public void testFailOnZuluIfStrict() + { + try { + STRICT_MAPPER.readValue(q("2020-10-22T00:16:20.504Z"), LocalDateTime.class); + fail("Should not pass"); + } catch (InvalidFormatException e) { + verifyException(e, "Cannot deserialize value of type "); + verifyException(e, "Should not contain offset when 'strict' mode"); + } + } + + @Test + public void testBadDeserializationAsString01() + { + try { + READER.readValue(q("notalocaldatetime")); + fail("expected fail"); + } catch (InvalidFormatException e) { + verifyException(e, "Cannot deserialize value of type"); + verifyException(e, "from String \""); + } + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() + { + String key = "datetime"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsNullStr = null; + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + LocalDateTime actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + LocalDateTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(actualDateFromNullStr, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "datetime"; + final ObjectReader objectReader = STRICT_MAPPER.readerFor(MAP_TYPE_REF); + final String dateValAsNullStr = null; + + // even with strict, null value should be deserialized without throwing an exception + String valueFromNullStr = STRICT_MAPPER.writeValueAsString(asMap(key, dateValAsNullStr)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String dateValAsEmptyStr = ""; + // TODO: nothing stops us from writing an empty string, maybe there should be a check there too? + String valueFromEmptyStr = STRICT_MAPPER.writeValueAsString(asMap("date", dateValAsEmptyStr)); + // with strict, deserializing an empty string is not permitted + try { + objectReader.readValue(valueFromEmptyStr); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize instance of `java.time.LocalDateTime` out of "); + } + } + + /* + /********************************************************** + /* Tests for alternate array handling + /********************************************************** + */ + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + READER.readValue("[\"2000-01-01T12:00\"]"); + } catch (MismatchedInputException e) { + verifyException(e, "Unexpected token (VALUE_STRING) within Array"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + // works even without the feature enabled + assertNull(READER.readValue("[]")); + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + LocalDateTime value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[\"2000-01-01T12:00\"]"); + assertEquals(LocalDateTime.of(2000, 1, 1, 12, 0, 0, 0), + value, "The value is not correct."); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + LocalDateTime value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + /* + /********************************************************** + /* Tests for polymorphic handling + /********************************************************** + */ + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + final ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,829837]]"); + assertTrue(value instanceof LocalDateTime, "The value should be a LocalDateTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 422000000); + + final ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,422]]"); + assertTrue(value instanceof LocalDateTime, "The value should be a LocalDateTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + final ObjectMapper m = newMapperBuilder(). + addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + LocalDateTime.class.getName() + "\",\"" + time.toString() + "\"]", Temporal.class + ); + assertTrue(value instanceof LocalDateTime, "The value should be a LocalDateTime."); + assertEquals(time, value, "The value is not correct."); + } + + /* + /********************************************************** + /* Tests for `DeserialiazationProblemHandler` usage + /********************************************************** + */ + + @Test + public void testDateTimeExceptionIsHandled() throws Throwable + { + LocalDateTime now = LocalDateTime.now(); + DeserializationProblemHandler handler = new DeserializationProblemHandler() { + @Override + public Object handleWeirdStringValue(DeserializationContext ctxt, Class targetType, + String valueToConvert, String failureMsg) { + if (LocalDateTime.class == targetType) { + if ("now".equals(valueToConvert)) { + return now; + } + } + return NOT_HANDLED; + } + }; + ObjectMapper handledMapper = mapperBuilder().addHandler(handler).build(); + assertEquals(now, handledMapper.readValue(q("now"), LocalDateTime.class)); + } + + @Test + public void testUnexpectedTokenIsHandled() throws Throwable + { + LocalDateTime now = LocalDateTime.now(); + DeserializationProblemHandler handler = new DeserializationProblemHandler() { + @Override + public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, + JsonToken t, JsonParser p, String failureMsg) { + if (targetType.hasRawClass(LocalDateTime.class)) { + if (t.isBoolean()) { + return now; + } + } + return NOT_HANDLED; + } + }; + ObjectMapper handledMapper = mapperBuilder().addHandler(handler).build(); + assertEquals(now, handledMapper.readValue("true", LocalDateTime.class)); + } + + /* + /********************************************************** + /* Tests for specific reported issues + /********************************************************** + */ + + // [datatype-jrs310#54] + @Test + public void testDeserializeToDate() throws Exception + { + ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + String localDateTimeJson = m.writeValueAsString(LocalDateTime.of(1999,10,12,13,45,5)); + assertEquals("\"1999-10-12T13:45:05\"", localDateTimeJson); + Date date = m.readValue(localDateTimeJson,Date.class); + assertNotNull(date); + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal.setTimeInMillis(date.getTime()); + assertEquals(1999, cal.get(Calendar.YEAR)); + assertEquals(12, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(13, cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(45, cal.get(Calendar.MINUTE)); + assertEquals(5, cal.get(Calendar.SECOND)); + } + + // [modules-java8#47]: should indicate why timestamp won't work + @Test + public void testDeserilizeFromSimpleTimestamp() throws Exception + { + ObjectReader r = MAPPER.readerFor(LocalDateTime.class); + LocalDateTime value; + try { + value = r.readValue("1235"); + fail("Should not succeed, instead got: "+value); + } catch (MismatchedInputException e) { + verifyException(e, "raw timestamp (1235) not allowed for `java.time.LocalDateTime`"); + } + } + + /* + /********************************************************************** + /* Case-insensitive tests + /********************************************************************** + */ + + // [modules-java8#80]: handle case-insensitive date/time + @Test + public void testDeserializationCaseInsensitiveEnabledOnValue() throws Throwable + { + final ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDateTime.class, o -> o.setFormat(JsonFormat.Value + .forPattern("dd-MMM-yyyy HH:mm") + .withFeature(Feature.ACCEPT_CASE_INSENSITIVE_VALUES))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDateTime.class); + String[] jsons = new String[] {"'01-Jan-2000 13:14'","'01-JAN-2000 13:14'", "'01-jan-2000 13:14'"}; + for(String json : jsons) { + expectSuccess(reader, LocalDateTime.of(2000, Month.JANUARY, 1, 13, 14), json); + } + } + + @Test + public void testDeserializationCaseInsensitiveEnabled() throws Throwable + { + final ObjectMapper mapper = newMapperBuilder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, true) + .withConfigOverride(LocalDateTime.class, o -> o.setFormat( + JsonFormat.Value.forPattern("dd-MMM-yyyy HH:mm"))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDateTime.class); + String[] jsons = new String[] {"'01-Jan-2000 13:45'","'01-JAN-2000 13:45'", "'01-jan-2000 13:45'"}; + for(String json : jsons) { + expectSuccess(reader, LocalDateTime.of(2000, Month.JANUARY, 1, 13, 45), json); + } + } + + @Test + public void testDeserializationCaseInsensitiveDisabled() throws Throwable + { + final ObjectMapper mapper = newMapperBuilder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false) + .withConfigOverride(LocalDateTime.class, o -> o.setFormat( + JsonFormat.Value.forPattern("dd-MMM-yyyy HH:mm"))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDateTime.class); + expectSuccess(reader, LocalDateTime.of(2000, Month.JANUARY, 1, 13, 45), + q("01-Jan-2000 13:45")); + } + + @Test + public void testDeserializationCaseInsensitiveDisabled_InvalidDate() throws Throwable + { + final ObjectMapper mapper = newMapperBuilder() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES, false) + .withConfigOverride(LocalDateTime.class, o -> o.setFormat( + JsonFormat.Value.forPattern("dd-MMM-yyyy"))) + .build(); + ObjectReader reader = mapper.readerFor(LocalDateTime.class); + String[] jsons = new String[] {"'01-JAN-2000'", "'01-jan-2000'"}; + for(String json : jsons) { + try { + reader.readValue(a2q(json)); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Failed to deserialize `java.time.LocalDateTime` (with format"); + } + } + } + + /* + /********************************************************************** + /* Strict JsonFormat tests + /********************************************************************** + */ + + // [modules-java8#148]: handle strict deserializaiton for date/time + @Test + public void testStrictCustomFormatForInvalidFormat() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapper w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 15:45\"}", StrictWrapper.class)); + } + + @Test + public void testStrictCustomFormatForInvalidFormatWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 15:45\"}", StrictWrapperWithYearOfEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidDateWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31 15:45 AD\"}", StrictWrapperWithYearOfEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidTimeWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 25:45 AD\"}", StrictWrapperWithYearOfEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidDateAndTimeWithEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearOfEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31 25:45 AD\"}", StrictWrapperWithYearOfEra.class)); + + } + + @Test + public void testStrictCustomFormatValidDateAndTimeWithEra() throws Exception + { + StrictWrapperWithYearOfEra w = MAPPER.readValue("{\"value\":\"2019-11-30 20:45 AD\"}", StrictWrapperWithYearOfEra.class); + + assertEquals(w.value, LocalDateTime.of(2019, 11, 30, 20, 45)); + } + + @Test + public void testStrictCustomFormatForInvalidFormatWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 15:45 AD\"}", StrictWrapperWithYearWithoutEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidTimeWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-30 25:45\"}", StrictWrapperWithYearWithoutEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidDateWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31 15:45\"}", StrictWrapperWithYearWithoutEra.class)); + } + + @Test + public void testStrictCustomFormatForInvalidDateAndTimeWithoutEra() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapperWithYearWithoutEra w =*/ MAPPER.readValue("{\"value\":\"2019-11-31 25:45\"}", StrictWrapperWithYearWithoutEra.class)); + } + + @Test + public void testStrictCustomFormatForValidDateAndTimeWithoutEra() throws Exception + { + StrictWrapperWithYearWithoutEra w = MAPPER.readValue("{\"value\":\"2019-11-30 20:45\"}", + StrictWrapperWithYearWithoutEra.class); + + assertEquals(w.value, LocalDateTime.of(2019, 11, 30, 20, 45)); + } + + // [datatype-jsr310#124] Issue serializing and deserializing LocalDateTime.MAX and LocalDateTime.MIN + @Test + public void testDeserializationOfLocalDateTimeMax() throws Exception + { + ObjectMapper enabledMapper = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build(); + _testLocalDateTimeRoundTrip(enabledMapper, LocalDateTime.MAX); + _testLocalDateTimeRoundTrip(enabledMapper, LocalDateTime.MIN); + + ObjectMapper disabledMapper = mapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build(); + _testLocalDateTimeRoundTrip(disabledMapper, LocalDateTime.MAX); + _testLocalDateTimeRoundTrip(disabledMapper, LocalDateTime.MIN); + } + + private void _testLocalDateTimeRoundTrip(ObjectMapper mapper, LocalDateTime localDateTime) + throws Exception + { + String ser = mapper.writeValueAsString(localDateTime); + LocalDateTime result = mapper.readValue(ser, LocalDateTime.class); + assertEquals(localDateTime, result); + } + + private void expectSuccess(ObjectReader reader, Object exp, String json) throws IOException { + final LocalDateTime value = reader.readValue(a2q(json)); + assertNotNull(value, "The value should not be null."); + assertEquals(exp, value, "The value is not correct."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalTimeDeserTest.java new file mode 100644 index 0000000000..0c6b052168 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalTimeDeserTest.java @@ -0,0 +1,343 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.LocalTime; +import java.time.temporal.Temporal; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; +import com.fasterxml.jackson.annotation.OptBoolean; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalTimeDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(LocalTime.class); + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + final static class StrictWrapper { + @JsonFormat(pattern="HH:mm", lenient = OptBoolean.FALSE) + public LocalTime value; + + public StrictWrapper() { } + public StrictWrapper(LocalTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public LocalTime value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(LocalTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public LocalTime value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(LocalTime v) { value = v; } + } + + @Test + public void testDeserializationAsTimestamp01() throws Exception + { + LocalTime time = LocalTime.of(15, 43); + LocalTime value = READER.readValue("[15,43]"); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp02() throws Exception + { + LocalTime time = LocalTime.of(9, 22, 57); + LocalTime value = READER.readValue("[9,22,57]"); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Nanoseconds() throws Exception + { + LocalTime value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[9,22,0,57]"); + assertEquals(LocalTime.of(9, 22, 0, 57), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Milliseconds() throws Exception + { + LocalTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[9,22,0,57]"); + assertEquals(LocalTime.of(9, 22, 0, 57000000), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Nanoseconds() throws Exception + { + LocalTime value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829837]"); + assertEquals(LocalTime.of(22, 31, 5, 829837), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds01() throws Exception + { + LocalTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829837]"); + assertEquals(LocalTime.of(22, 31, 5, 829837), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds02() throws Exception + { + LocalTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829]"); + assertEquals(LocalTime.of(22, 31, 5, 829000000), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Nanoseconds() throws Exception + { + ObjectReader wrapperReader = + newMapper().readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + WrapperWithReadTimestampsAsNanosEnabled actual = wrapperReader + .readValue(a2q("{'value':[9,22,0,57]}")); + assertEquals(LocalTime.of(9, 22, 0, 57), actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds01() throws Exception + { + ObjectReader wrapperReader = + newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + WrapperWithReadTimestampsAsNanosDisabled actual = wrapperReader + .readValue(a2q("{'value':[9,22,0,57]}")); + assertEquals(LocalTime.of(9, 22, 0, 57000000), actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds02() throws Exception + { + ObjectReader wrapperReader = + newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + WrapperWithReadTimestampsAsNanosDisabled actual = wrapperReader + .readValue(a2q("{'value':[9,22,0,4257]}")); + assertEquals(LocalTime.of(9, 22, 0, 4257), actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationFromString() throws Exception + { + LocalTime time = LocalTime.of(15, 43); + LocalTime value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + + expectSuccess(LocalTime.of(12, 0), "'12:00'"); + + time = LocalTime.of(9, 22, 57); + value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + + time = LocalTime.of(22, 31, 5, 829837); + value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testBadDeserializationFromString() throws Throwable + { + try { + READER.readValue(q("notalocaltime")); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.LocalTime` from String"); + } + } + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + READER.readValue(a2q("['12:00']")); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Unexpected token (VALUE_STRING) within Array"); + } + + // 25-Jul-2017, tatu: Why does it work? Is it supposed to? + // works even without the feature enabled + assertNull(READER.readValue("[]")); + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + LocalTime value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue(a2q("['12:00']")); + expect(LocalTime.of(12, 0), value); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + LocalTime value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue(a2q("[]")); + assertNull(value); + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + LocalTime.class.getName() + "\",[22,31,5,829837]]"); + + assertNotNull(value, "The value should not be null."); + assertTrue(value instanceof LocalTime, "The value should be a LocalTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 422000000); + + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + LocalTime.class.getName() + "\",[22,31,5,422]]"); + assertTrue(value instanceof LocalTime, "The value should be a LocalTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue( + "[\"" + LocalTime.class.getName() + "\",\"" + time.toString() + "\"]", Temporal.class + ); + assertTrue(value instanceof LocalTime, "The value should be a LocalTime."); + assertEquals(time, value, "The value is not correct."); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "localTime"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + LocalTime actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + LocalTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "localTime"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(LocalTime.class, + c -> c.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", "")); + assertThrows(MismatchedInputException.class, + () -> objectReader.readValue(valueFromEmptyStr)); + } + + /* + /********************************************************************** + /* Strict JsonFormat tests + /********************************************************************** + */ + + // [modules-java8#148]: handle strict deserializaiton for date/time + + @Test + public void testStrictCustomFormatInvalidTime() throws Exception + { + assertThrows(InvalidFormatException.class, + () -> /*StrictWrapper w =*/ MAPPER.readValue("{\"value\":\"25:45\"}", StrictWrapper.class)); + } + + private void expectSuccess(Object exp, String aposJson) throws Exception { + final LocalTime value = READER.readValue(a2q(aposJson)); + expect(exp, value); + } + + private static void expect(Object exp, Object value) { + assertEquals(exp, value, "The value is not correct."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/MonthDayDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/MonthDayDeserTest.java new file mode 100644 index 0000000000..9b127cde26 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/MonthDayDeserTest.java @@ -0,0 +1,211 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Month; +import java.time.MonthDay; +import java.time.temporal.TemporalAccessor; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class MonthDayDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(MonthDay.class); + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + static class Wrapper { + @JsonFormat(pattern="MM/dd") + public MonthDay value; + + public Wrapper(MonthDay v) { value = v; } + public Wrapper() { } + } + + static class WrapperAsArray { + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + public MonthDay value; + + public WrapperAsArray(MonthDay v) { value = v; } + public WrapperAsArray() { } + } + + + @Test + public void testDeserializationAsString01() throws Exception + { + expectSuccess(MonthDay.of(Month.JANUARY, 1), "'--01-01'"); + } + + @Test + public void testBadDeserializationAsString01() throws Throwable + { + try { + READER.readValue(q("notamonthday")); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.MonthDay` from String"); + } + } + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + read("['--01-01']"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + // expecting array-of-ints + verifyException(e, "Unexpected token"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + // since 2.10, empty array taken as `null` + + MonthDay value = READER.readValue("[]"); + assertNull(value); + + value = newMapper() + .readerFor(MonthDay.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[]"); + assertNull(value); + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + MonthDay value = READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue(a2q("['--01-01']")); + expect(MonthDay.of(Month.JANUARY, 1), value); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + MonthDay value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + @Test + public void testDeserialization01() throws Exception + { + assertEquals(MonthDay.of(Month.JANUARY, 17), MAPPER.readValue("\"--01-17\"", MonthDay.class), + "The value is not correct."); + } + + @Test + public void testDeserialization02() throws Exception + { + assertEquals(MonthDay.of(Month.AUGUST, 21), + MAPPER.readValue("\"--08-21\"", MonthDay.class), + "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + final ObjectMapper mapper = mapperBuilder() + .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .build(); + MonthDay monthDay = MonthDay.of(Month.NOVEMBER, 5); + TemporalAccessor value = mapper.readValue("[\"" + MonthDay.class.getName() + "\",\"--11-05\"]", TemporalAccessor.class); + assertEquals(monthDay, value, "The value is not correct."); + } + + @Test + public void testFormatAnnotation() throws Exception + { + final Wrapper input = new Wrapper(MonthDay.of(12, 28)); + String json = MAPPER.writeValueAsString(input); + assertEquals("{\"value\":\"12/28\"}", json); + + Wrapper output = MAPPER.readValue(json, Wrapper.class); + assertEquals(input.value, output.value); + } + + @Test + public void testFormatAnnotationArray() throws Exception + { + final WrapperAsArray input = new WrapperAsArray(MonthDay.of(12, 28)); + String json = MAPPER.writeValueAsString(input); + assertEquals("{\"value\":[12,28]}", json); + + // 13-May-2019, tatu: [modules-java#107] not fully implemented so can't yet test + WrapperAsArray output = MAPPER.readValue(json, WrapperAsArray.class); + assertEquals(input.value, output.value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + // minor changes in 2.12 + @Test + public void testDeserializeFromEmptyString() throws Exception + { + final String key = "monthDay"; + + // First: by default, lenient, so empty String fine + final ObjectReader objectReader = MAPPER.readerFor(MAP_TYPE_REF); + + String doc = MAPPER.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(doc); + assertNull(actualMapFromNullStr.get(key)); + + doc = MAPPER.writeValueAsString(asMap(key, "")); + assertNotNull(objectReader.readValue(doc)); + + // But can make strict: + final ObjectMapper strictMapper = mapperBuilder() + .withConfigOverride(MonthDay.class, o -> o.setFormat( + JsonFormat.Value.forLeniency(false))) + .build(); + doc = strictMapper.writeValueAsString(asMap("date", "")); + try { + strictMapper.readerFor(MAP_TYPE_REF) + .readValue(doc); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "not allowed because 'strict' mode set for"); + } + } + + private void expectSuccess(Object exp, String aposJson) throws Exception { + final MonthDay value = read(aposJson); + notNull(value); + expect(exp, value); + } + + private MonthDay read(final String aposJson) throws Exception { + return READER.readValue(a2q(aposJson)); + } + + private static void notNull(Object value) { + assertNotNull(value, "The value should not be null."); + } + + private static void expect(Object exp, Object value) { + assertEquals(exp, value, "The value is not correct."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/OffsetDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/OffsetDateTimeDeserTest.java new file mode 100644 index 0000000000..7d21c92041 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/OffsetDateTimeDeserTest.java @@ -0,0 +1,824 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.Arrays; +import java.util.Map; +import java.util.TimeZone; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.datetime.util.DecimalUtils; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetDateTimeDeserTest + extends ModuleTestBase +{ + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + private static final ZoneId Z1 = ZoneId.of("America/Chicago"); + + private static final ZoneId Z2 = ZoneId.of("America/Anchorage"); + + private static final ZoneId Z3 = ZoneId.of("America/Los_Angeles"); + + final static class Wrapper { + @JsonFormat( + pattern="yyyy_MM_dd'T'HH:mm:ssZ", + shape=JsonFormat.Shape.STRING) + public OffsetDateTime value; + + public Wrapper() { } + public Wrapper(OffsetDateTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public OffsetDateTime value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(OffsetDateTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public OffsetDateTime value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(OffsetDateTime v) { value = v; } + } + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectMapper MAPPER_DEFAULT_TZ = newMapper(TimeZone.getDefault()); + + @Test + public void testDeserializationAsFloat01WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER.readValue("0.000000000", OffsetDateTime.class); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat01WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readValue("0.000000000", OffsetDateTime.class); + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat02WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + OffsetDateTime value = MAPPER.readValue("123456789.183917322", OffsetDateTime.class); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat02WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(TimeZone.getDefault()) + .readValue("123456789.183917322"); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat03WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + + OffsetDateTime value = MAPPER + .readerFor(OffsetDateTime.class) + .readValue(DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano())); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat03WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .readValue(DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano())); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01NanosecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01NanosecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .with(TimeZone.getDefault()) + .readValue("0"); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01MillisecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01MillisecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02NanosecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789"); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02NanosecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789"); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02MillisecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789422"); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02MillisecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789422"); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03NanosecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toEpochSecond())); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03NanosecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toEpochSecond())); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03MillisecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + date = date.minus(date.getNano() - (date.get(ChronoField.MILLI_OF_SECOND) * 1_000_000L), ChronoUnit.NANOS); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toInstant().toEpochMilli())); + + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03MillisecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + date = date.minus(date.getNano() - (date.get(ChronoField.MILLI_OF_SECOND) * 1_000_000L), ChronoUnit.NANOS); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toInstant().toEpochMilli())); + + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt04NanosecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z1); + ObjectMapper m = newMapper(); + WrapperWithReadTimestampsAsNanosEnabled actual = m.readValue( + a2q("{'value':123456789}"), + WrapperWithReadTimestampsAsNanosEnabled.class); + + assertNotNull(actual, "The actual should not be null."); + assertNotNull(actual.value, "The actual value should not be null."); + assertIsEqual(date, actual.value); + assertEquals(ZoneOffset.UTC, actual.value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt04NanosecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z1); + ObjectMapper m = mapperBuilder() + .defaultTimeZone(TimeZone.getDefault()) + .build(); + WrapperWithReadTimestampsAsNanosEnabled actual = m.readValue( + a2q("{'value':123456789}"), + WrapperWithReadTimestampsAsNanosEnabled.class); + + assertNotNull(actual, "The actual should not be null."); + assertNotNull(actual.value, "The actual value should not be null."); + assertIsEqual(date, actual.value); + assertEquals(getDefaultOffset(date), actual.value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt04MillisecondsWithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z1); + ObjectMapper m = newMapper(); + WrapperWithReadTimestampsAsNanosDisabled actual = m.readValue( + a2q("{'value':123456789422}"), + WrapperWithReadTimestampsAsNanosDisabled.class); + + assertNotNull(actual, "The actual should not be null."); + assertNotNull(actual.value, "The actual value should not be null."); + assertIsEqual(date, actual.value); + assertEquals(ZoneOffset.UTC, actual.value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt04MillisecondsWithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z1); + ObjectMapper m = mapperBuilder() + .defaultTimeZone(TimeZone.getDefault()) + .build(); + WrapperWithReadTimestampsAsNanosDisabled actual = m.readValue( + a2q("{'value':123456789422}"), + WrapperWithReadTimestampsAsNanosDisabled.class); + + assertNotNull(actual, "The actual should not be null."); + assertNotNull(actual.value, "The actual value should not be null."); + assertIsEqual(date, actual.value); + assertEquals(getDefaultOffset(date), actual.value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithTimeZoneTurnedOff() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(getOffset(value, Z1), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithTimeZoneColonless() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ObjectReader r = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + + String sDate = offsetWithoutColon(FORMATTER.format(date)); + + OffsetDateTime value = r.readValue('"' + sDate + '"'); + + assertIsEqual(date, value); + assertEquals(getOffset(value, Z1), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithTimeZoneTurnedOff() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + + assertIsEqual(date, value); + assertEquals(getOffset(value, Z2), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithTimeZoneColonless() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ObjectReader r = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + + String sDate = offsetWithoutColon(FORMATTER.format(date)); + + OffsetDateTime value = r.readValue('"' + sDate + '"'); + + assertIsEqual(date, value); + assertEquals(getOffset(value, Z2), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + OffsetDateTime value = MAPPER.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneOffset.UTC, value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(getDefaultOffset(date), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithTimeZoneTurnedOff() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + OffsetDateTime value = MAPPER_DEFAULT_TZ.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(getOffset(value, Z3), value.getOffset(), "The time zone is not correct."); + } + + + @Test + public void testDeserializationAsString03WithTimeZoneColonless() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + ObjectReader r = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + + String sDate = offsetWithoutColon(FORMATTER.format(date)); + + OffsetDateTime value = r.readValue('"' + sDate + '"'); + + assertIsEqual(date, value); + assertEquals(getOffset(value, Z3), value.getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo01WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789.183917322]", Temporal.class + ); + + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(ZoneOffset.UTC, ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo01WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ObjectMapper m = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789.183917322]", Temporal.class + ); + + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(getDefaultOffset(date), ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(ZoneOffset.UTC, ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + ObjectMapper m = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(getDefaultOffset(date), ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789422]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(ZoneOffset.UTC, ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + ObjectMapper m = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",123456789422]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(getDefaultOffset(date), ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithoutTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(ZoneOffset.UTC, ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + ObjectMapper m = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + assertIsEqual(date, (OffsetDateTime) value); + assertEquals(getDefaultOffset(date), ((OffsetDateTime) value).getOffset(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithTimeZoneTurnedOff() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + + ObjectMapper m = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readerFor(Temporal.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + OffsetDateTime cast = (OffsetDateTime) value; + assertIsEqual(date, cast); + assertEquals(getOffset(cast, Z3), cast.getOffset(), "The time zone is not correct."); + } + + @Test + public void testCustomPatternWithAnnotations() throws Exception + { + OffsetDateTime inputValue = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), UTC); + final Wrapper input = new Wrapper(inputValue); + String json = MAPPER.writeValueAsString(input); + assertEquals(a2q("{'value':'1970_01_01T00:00:00+0000'}"), json); + + Wrapper result = MAPPER.readValue(json, Wrapper.class); + assertEquals(input.value, result.value); + } + + // [datatype-jsr310#79] + @Test + public void testRoundTripOfOffsetDateTimeAndJavaUtilDate() throws Exception + { + Instant givenInstant = LocalDate.of(2016, 1, 1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(); + + String json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, + SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(java.util.Date.from(givenInstant)); + OffsetDateTime actual = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(json); + assertEquals(givenInstant.atOffset(ZoneOffset.UTC), actual); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "OffsetDateTime"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + OffsetDateTime actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + OffsetDateTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "OffsetDateTime"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(OffsetDateTime.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + // [module-java8#166] + @Test + public void testDeserializationNoAdjustIfMIN() throws Exception + { + OffsetDateTime date = OffsetDateTime.MIN; + ObjectMapper m = mapperBuilder() + .enable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .defaultTimeZone(TimeZone.getTimeZone(Z1)) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class + ); + + assertTrue(value instanceof OffsetDateTime, "The value should be an OffsetDateTime."); + OffsetDateTime actualValue = (OffsetDateTime) value; + assertIsEqual(date, actualValue); + assertEquals(date.getOffset(),actualValue.getOffset()); + } + + @Test + public void testDeserializationNoAdjustIfMAX() throws Exception + { + OffsetDateTime date = OffsetDateTime.MAX; + ObjectMapper m = mapperBuilder() + .enable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .defaultTimeZone(TimeZone.getTimeZone(Z1)) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = m.readValue( + "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", Temporal.class + ); + + assertInstanceOf(OffsetDateTime.class, value, "The value should be an OffsetDateTime."); + OffsetDateTime actualValue = (OffsetDateTime) value; + assertIsEqual(date, actualValue); + assertEquals(date.getOffset(),actualValue.getOffset()); + } + + // [jackson-modules-java8#308] Can't deserialize OffsetDateTime.MIN: Invalid value for EpochDay + @Test + public void testOffsetDateTimeMinOrMax() throws Exception + { + _testOffsetDateTimeMinOrMax(OffsetDateTime.MIN); + _testOffsetDateTimeMinOrMax(OffsetDateTime.MAX); + } + + @Test + public void OffsetDateTime_with_offset_can_be_deserialized() throws Exception { + ObjectReader r = MAPPER.readerFor(OffsetDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + + String base = "2015-07-24T12:23:34.184"; + for (String offset : Arrays.asList("+00", "-00")) { + String time = base + offset; + if (!System.getProperty("java.version").startsWith("1.8")) { + // JDK 8 cannot parse hour offsets without minutes + assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + '"')); + } + assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + "00" + '"')); + assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + ":00" + '"')); + assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30"), r.readValue('"' + time + "30" + '"')); + assertIsEqual(OffsetDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30"), r.readValue('"' + time + ":30" + '"')); + } + + for (String prefix : Arrays.asList("-", "+")) { + for (String hours : Arrays.asList("00", "01", "02", "03", "11", "12")) { + String time = base + prefix + hours; + OffsetDateTime expectedHour = OffsetDateTime.parse(time + ":00"); + if (!System.getProperty("java.version").startsWith("1.8")) { + // JDK 8 cannot parse hour offsets without minutes + assertIsEqual(expectedHour, r.readValue('"' + time + '"')); + } + assertIsEqual(expectedHour, r.readValue('"' + time + "00" + '"')); + assertIsEqual(expectedHour, r.readValue('"' + time + ":00" + '"')); + assertIsEqual(OffsetDateTime.parse(time + ":30"), r.readValue('"' + time + "30" + '"')); + assertIsEqual(OffsetDateTime.parse(time + ":30"), r.readValue('"' + time + ":30" + '"')); + } + } + } + + private void _testOffsetDateTimeMinOrMax(OffsetDateTime offsetDateTime) + throws Exception + { + String ser = MAPPER.writeValueAsString(offsetDateTime); + OffsetDateTime result = MAPPER.readValue(ser, OffsetDateTime.class); + assertIsEqual(offsetDateTime, result); + } + + private static void assertIsEqual(OffsetDateTime expected, OffsetDateTime actual) + { + assertTrue(expected.isEqual(actual), + "The value is not correct. Expected timezone-adjusted <" + expected + ">, actual <" + actual + ">."); + } + + private static ZoneOffset getDefaultOffset(OffsetDateTime date) + { + return ZoneId.systemDefault().getRules().getOffset(date.toLocalDateTime()); + } + + private static ZoneOffset getOffset(OffsetDateTime date, ZoneId zone) + { + return zone.getRules().getOffset(date.toLocalDateTime()); + } + + private static String offsetWithoutColon(String string){ + return new StringBuilder(string).deleteCharAt(string.lastIndexOf(":")).toString(); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserTest.java new file mode 100644 index 0000000000..30b017684e --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/OffsetTimeDeserTest.java @@ -0,0 +1,350 @@ +package tools.jackson.databind.datetime.deser; + +import java.io.IOException; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.temporal.Temporal; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetTimeDeserTest extends ModuleTestBase +{ + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + // for [datatype-jsr310#45] + static class Pojo45s { + public String name; + public List objects; + } + + static class Pojo45 { + public java.time.LocalDate partDate; + public java.time.OffsetTime starttime; + public java.time.OffsetTime endtime; + public String comments; + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public OffsetTime value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(OffsetTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public OffsetTime value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(OffsetTime v) { value = v; } + } + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(OffsetTime.class); + + @Test + public void testDeserializationAsTimestamp01() throws Exception + { + OffsetTime time = OffsetTime.of(15, 43, 0, 0, ZoneOffset.of("+0300")); + OffsetTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[15,43,\"+0300\"]"); + + assertNotNull(value, "The value should not be null."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp02() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 57, 0, ZoneOffset.of("-0630")); + OffsetTime value = READER.readValue("[9,22,57,\"-06:30\"]"); + + assertNotNull(value, "The value should not be null."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Nanoseconds() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 0, 57, ZoneOffset.of("-0630")); + OffsetTime value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[9,22,0,57,\"-06:30\"]"); + + assertNotNull(value, "The value should not be null."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp03Milliseconds() throws Exception { + OffsetTime time = OffsetTime.of(9, 22, 0, 57000000, ZoneOffset.of("-0630")); + OffsetTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[9,22,0,57,\"-06:30\"]"); + + assertNotNull(value, "The value should not be null."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Nanoseconds() throws Exception { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + OffsetTime value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829837,\"+11:00\"]"); + + assertNotNull(value, "The value should not be null."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds01() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + OffsetTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829837,\"+11:00\"]"); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp04Milliseconds02() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829000000, ZoneOffset.of("+1100")); + OffsetTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[22,31,5,829,\"+11:00\"]"); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Nanoseconds() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + OffsetTime time = OffsetTime.of(9, 22, 0, 57, ZoneOffset.of("-0630")); + WrapperWithReadTimestampsAsNanosEnabled actual = reader + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(a2q("{'value':[9,22,0,57,'-06:30']}")); + + assertNotNull(actual, "The value should not be null."); + assertEquals(time, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds01() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + OffsetTime time = OffsetTime.of(9, 22, 0, 57000000, ZoneOffset.of("-0630")); + WrapperWithReadTimestampsAsNanosDisabled actual = reader + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(a2q("{'value':[9,22,0,57,'-06:30']}")); + + assertNotNull(actual, "The value should not be null."); + assertEquals(time, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsTimestamp05Milliseconds02() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + OffsetTime time = OffsetTime.of(9, 22, 0, 4257, ZoneOffset.of("-0630")); + WrapperWithReadTimestampsAsNanosDisabled actual = reader + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(a2q("{'value':[9,22,0,4257,'-06:30']}")); + + assertNotNull(actual, "The value should not be null."); + assertEquals(time, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationFromString01() throws Exception + { + OffsetTime time = OffsetTime.of(15, 43, 0, 0, ZoneOffset.of("+0300")); + OffsetTime value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + + time = OffsetTime.of(9, 22, 57, 0, ZoneOffset.of("-0630")); + value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + + time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + value = READER.readValue('"' + time.toString() + '"'); + assertEquals(time, value, "The value is not correct."); + + assertEquals(OffsetTime.of(12, 0, 0, 0, ZoneOffset.UTC), + READER.readValue(a2q("'12:00Z'"))); + } + + @Test + public void testBadDeserializationFromString01() throws Throwable + { + try { + READER.readValue(q("notanoffsettime")); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.OffsetTime` from String"); + } + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + OffsetTime.class.getName() + "\",[22,31,5,829837,\"+11:00\"]]"); + assertInstanceOf(OffsetTime.class, value, "The value should be a OffsetTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 422000000, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + OffsetTime.class.getName() + "\",[22,31,5,422,\"+11:00\"]]"); + + assertNotNull(value, "The value should not be null."); + assertInstanceOf(OffsetTime.class, value, "The value should be a OffsetTime."); + assertEquals(time, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue( + "[\"" + OffsetTime.class.getName() + "\",\"" + time.toString() + "\"]", Temporal.class + ); + assertTrue(value instanceof OffsetTime, "The value should be a OffsetTime."); + assertEquals(time, value, "The value is not correct."); + } + + // for [datatype-jsr310#45] + @Test + public void testDeserOfArrayOf() throws Exception + { + final String JSON = a2q + ("{'name':'test','objects':[{'partDate':[2015,10,13],'starttime':[15,7,'+0'],'endtime':[2,14,'+0'],'comments':'in the comments'}]}"); + Pojo45s result = READER.forType(Pojo45s.class).readValue(JSON); + assertNotNull(result); + assertNotNull(result.objects); + assertEquals(1, result.objects.size()); + } + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + READER.readValue(a2q("['12:00Z']")); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + // not the greatest error message... + verifyException(e, "Unexpected token (VALUE_STRING) within Array, expected"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + // works even without the feature enabled + assertNull(READER.readValue(a2q("[]"))); + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + OffsetTime value = READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue(a2q("['12:00Z']")); + assertEquals(OffsetTime.of(12, 0, 0, 0, ZoneOffset.UTC), value); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + OffsetTime value = READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "OffsetTime"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + OffsetTime actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + OffsetTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "OffsetTime"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(OffsetTime.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java new file mode 100644 index 0000000000..b1f16a9a99 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java @@ -0,0 +1,193 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.Month; +import java.time.temporal.TemporalAccessor; + +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class OneBasedMonthDeserTest extends ModuleTestBase +{ + static class Wrapper { + public Month value; + + public Wrapper(Month v) { value = v; } + public Wrapper() { } + } + + @Test + public void testDeserializationAsString01_oneBased() throws Exception + { + assertEquals(Month.JANUARY, readerForOneBased().readValue("\"1\"")); + } + + @Test + public void testDeserializationAsString01_zeroBased() throws Exception + { + assertEquals(Month.FEBRUARY, readerForZeroBased().readValue("\"1\"")); + } + + + @Test + public void testDeserializationAsString02_oneBased() throws Exception + { + assertEquals(Month.JANUARY, readerForOneBased().readValue("\"JANUARY\"")); + } + + @Test + public void testDeserializationAsString02_zeroBased() throws Exception + { + assertEquals(Month.JANUARY, readerForZeroBased().readValue("\"JANUARY\"")); + } + + @Test + public void testBadDeserializationAsString01_oneBased() { + assertError( + () -> readerForOneBased().readValue("\"notamonth\""), + InvalidFormatException.class, + // Order of enumerated values not stable, so don't check: + "Cannot deserialize value of type `java.time.Month` from String \"notamonth\":" + +" not one of the values accepted for Enum class: [" + ); + } + + static void assertError(Executable codeToRun, Class expectedException, String expectedMessage) { + try { + codeToRun.execute(); + fail(String.format("Expecting %s, but nothing was thrown!", expectedException.getName())); + } catch (Throwable actualException) { + if (!expectedException.isInstance(actualException)) { + fail(String.format("Expecting exception of type %s, but %s was thrown instead", expectedException.getName(), actualException.getClass().getName())); + } + if (actualException.getMessage() == null || !actualException.getMessage().contains(expectedMessage)) { + fail(String.format("Expecting exception with message containing: '%s', but the actual error message was:'%s'", expectedMessage, actualException.getMessage())); + } + } + } + + + @Test + public void testDeserialization01_zeroBased() throws Exception + { + assertEquals(Month.FEBRUARY, readerForZeroBased().readValue("1")); + } + + @Test + public void testDeserialization01_oneBased() throws Exception + { + assertEquals(Month.JANUARY, readerForOneBased().readValue("1")); + } + + @Test + public void testDeserialization02_zeroBased() throws Exception + { + assertEquals(Month.SEPTEMBER, readerForZeroBased().readValue("\"8\"")); + } + + @Test + public void testDeserialization02_oneBased() throws Exception + { + assertEquals(Month.AUGUST, readerForOneBased().readValue("\"8\"")); + } + + @Test + public void testDeserializationWithTypeInfo01_oneBased() throws Exception + { + ObjectMapper MAPPER = JsonMapper.builder() + .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .build(); + + TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",11]", TemporalAccessor.class); + assertEquals(Month.NOVEMBER, value); + } + + @Test + public void testDeserializationWithTypeInfo01_zeroBased() throws Exception + { + ObjectMapper MAPPER = JsonMapper.builder() + .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .build(); + + TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",\"11\"]", TemporalAccessor.class); + assertEquals(Month.DECEMBER, value); + } + + @Test + public void testFormatAnnotation_zeroBased() throws Exception + { + Wrapper output = readerForZeroBased() + .forType(Wrapper.class) + .readValue("{\"value\":\"11\"}"); + assertEquals(new Wrapper(Month.DECEMBER).value, output.value); + } + + @Test + public void testFormatAnnotation_oneBased() throws Exception + { + Wrapper output = readerForOneBased() + .forType(Wrapper.class) + .readValue("{\"value\":\"11\"}"); + assertEquals(new Wrapper(Month.NOVEMBER).value, output.value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testDeserializeFromEmptyString() throws Exception + { + final ObjectMapper mapper = newMapper(); + + // Nulls are handled in general way, not by deserializer so they are ok + Month m = mapper.readerFor(Month.class).readValue(" null "); + assertNull(m); + + // But coercion from empty String not enabled for Enums by default: + try { + mapper.readerFor(Month.class).readValue("\"\""); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + } + // But can allow coercion of empty String to, say, null + ObjectMapper emptyStringMapper = mapperBuilder() + .withCoercionConfig(Month.class, + h -> h.setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull)) + .build(); + m = emptyStringMapper.readerFor(Month.class).readValue("\"\""); + assertNull(m); + } + + private ObjectReader readerForZeroBased() { + return JsonMapper.builder() + .addModule(new JavaTimeModule() + .disable(JavaTimeFeature.ONE_BASED_MONTHS)) + .build() + .readerFor(Month.class); + } + + private ObjectReader readerForOneBased() { + return JsonMapper.builder() + .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .build() + .readerFor(Month.class); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/PeriodDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/PeriodDeserTest.java new file mode 100644 index 0000000000..8a197f715c --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/PeriodDeserTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.Period; +import java.time.temporal.TemporalAmount; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.type.LogicalType; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class PeriodDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + @Test + public void testDeserialization01() throws Exception + { + Period period = Period.of(1, 6, 15); + Period value = MAPPER.readValue('"' + period.toString() + '"', Period.class); + assertEquals(period, value, "The value is not correct."); + } + + @Test + public void testDeserialization02() throws Exception + { + Period period = Period.of(0, 0, 21); + Period value = MAPPER.readValue('"' + period.toString() + '"', Period.class); + assertEquals(period, value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + Period period = Period.of(5, 1, 12); + + final ObjectMapper mapper = mapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + TemporalAmount value = mapper.readValue( + "[\"" + Period.class.getName() + "\",\"" + period.toString() + "\"]", TemporalAmount.class + ); + + assertNotNull(value, "The value should not be null."); + assertInstanceOf(Period.class, value, "The value should be a Period."); + assertEquals(period, value, "The value is not correct."); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "period"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + Period actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + Period actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "period"; + final ObjectMapper mapper = mapperBuilder() + .withCoercionConfig(LogicalType.DateTime, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail)) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", "")); + try { + objectReader.readValue(valueFromEmptyStr); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + } + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/YearDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/YearDeserTest.java new file mode 100644 index 0000000000..9953786984 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/YearDeserTest.java @@ -0,0 +1,252 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.Year; +import java.time.temporal.Temporal; +import java.util.Map; +import java.util.Objects; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearDeserTest extends ModuleTestBase +{ + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + static class FormattedYear { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "'Y'yyyy") + public Year value; + + protected FormattedYear() {} + public FormattedYear(Year year) { + value = year; + } + } + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(Year.class); + + @Test + public void testDeserializationAsString01() + { + assertEquals(Year.of(2000), READER.readValue(q("2000")), + "The value is not correct."); + } + + @Test + public void testBadDeserializationAsString01() + { + try { + READER.readValue(q("notayear")); + fail("expected DateTimeParseException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Year` from String \"notayear\""); + } + } + + @Test + public void testDeserializationAsArrayDisabled() + { + try { + read("['2000']"); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Year` from Array"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() + { + try { + read("[]"); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Year` from Array"); + } + try { + READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[]"); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.Year` from Array"); + } + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + Year value= newMapper() + .readerFor(Year.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue(a2q("['2000']")); + assertEquals(Year.of(2000), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + Year value = newMapper() + .readerFor(Year.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + @Test + public void testDefaultDeserialization() throws Exception + { + Year value = READER.readValue("1986"); + assertEquals(Year.of(1986), value, "The value is not correct."); + value = READER.readValue("2013"); + assertEquals(Year.of(2013), value, "The value is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue("[\"" + Year.class.getName() + "\",2005]", Temporal.class); + assertTrue(value instanceof Year, "The value should be a Year."); + assertEquals(Year.of(2005), value, "The value is not correct."); + } + + @Test + public void testWithCustomFormat() throws Exception + { + FormattedYear input = new FormattedYear(Year.of(2018)); + String json = MAPPER.writeValueAsString(input); + assertEquals("{\"value\":\"Y2018\"}", json); + FormattedYear result = MAPPER.readValue(json, FormattedYear.class); + assertEquals(input.value, result.value); + } + + @Test + public void testWithFormatViaConfigOverride() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(Year.class, + vc -> vc.setFormat((JsonFormat.Value.forPattern("'X'yyyy")))) + .build(); + Year input = Year.of(2018); + String json = mapper.writeValueAsString(input); + assertEquals("\"X2018\"", json); + Year result = mapper.readValue(json, Year.class); + assertEquals(input, result); + } + + /* + /********************************************************** + /* Tests for specific issues + /********************************************************** + */ + + // [module-java8#78] + final static class ObjectTest { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "'Y'yyyy") + public Year value; + + protected ObjectTest() { } + public ObjectTest(Year y) { + value = y; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + ObjectTest other = (ObjectTest) o; + return Objects.equals(this.value, other.value); + } + + // stupid Javac 8 barfs on override missing?! + @Override + public int hashCode() { return 42; } + } + + // [module-java8#78] + @Test + public void testWithCustomFormat78() throws Exception + { + ObjectTest input = new ObjectTest(Year.of(2018)); + String json = MAPPER.writeValueAsString(input); + assertEquals("{\"value\":\"Y2018\"}", json); + ObjectTest result = MAPPER.readValue(json, ObjectTest.class); + assertEquals(input, result); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + // minor changes in 2.12 + @Test + public void testDeserializeFromEmptyString() throws Exception + { + final String key = "Year"; + final ObjectReader objectReader = MAPPER.readerFor(MAP_TYPE_REF); + + // First: by default, lenient, so empty String fine + String doc = MAPPER.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(doc); + assertNull(actualMapFromNullStr.get(key)); + + doc = MAPPER.writeValueAsString(asMap("date", "")); + Map actualMapFromEmptyStr = objectReader.readValue(doc); + assertNotNull(actualMapFromEmptyStr); + + // But can make strict: + final ObjectMapper strictMapper = mapperBuilder() + .withConfigOverride(Year.class, o -> o.setFormat( + JsonFormat.Value.forLeniency(false))) + .build(); + doc = strictMapper.writeValueAsString(asMap("date", "")); + try { + actualMapFromEmptyStr = strictMapper.readerFor(MAP_TYPE_REF) + .readValue(doc); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "not allowed because 'strict' mode set for"); + } + } + + /* + /********************************************************** + /* Helper methods + /********************************************************** + */ + + private Year read(final String json) { + return READER.readValue(a2q(json)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/YearMonthDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/YearMonthDeserTest.java new file mode 100644 index 0000000000..4050cd037f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/YearMonthDeserTest.java @@ -0,0 +1,145 @@ +package tools.jackson.databind.datetime.deser; + +import java.io.IOException; +import java.time.Month; +import java.time.YearMonth; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearMonthDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(YearMonth.class); + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + @Test + public void testDeserializationAsString01() throws Exception + { + final YearMonth value = read("'2000-01'"); + assertEquals(YearMonth.of(2000, Month.JANUARY), value, + "The value is not correct"); + } + + @Test + public void testBadDeserializationAsString01() throws Exception + { + try { + read(q("notayearmonth")); + fail("expected DateTimeParseException"); + } catch (InvalidFormatException e) { + verifyException(e, "could not be parsed"); + } + } + + @Test + public void testDeserializationAsArrayDisabled() throws Exception + { + try { + read("['2000-01']"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, +"Unexpected token (`JsonToken.VALUE_STRING`), expected `JsonToken.VALUE_NUMBER_INT`"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Exception + { + // works even without the feature enabled + assertNull(read("[]")); + } + + @Test + public void testDeserializationAsArrayEnabled() throws Exception + { + YearMonth value = READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue(a2q("['2000-01']")); + assertEquals(YearMonth.of(2000, Month.JANUARY), value, + "The value is not correct"); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Exception + { + YearMonth value = READER.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue( "[]"); + assertNull(value); + } + + // [modules-java8#249] + @Test + public void testYearAbove10k() throws Exception + { + YearMonth input = YearMonth.of(10000, 1); + String json = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(input); + YearMonth result = READER.readValue(json); + assertEquals(input, result); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "yearMonth"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String dateValAsEmptyStr = ""; + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + YearMonth actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, dateValAsEmptyStr)); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + YearMonth actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertNull(actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "YearMonth"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(YearMonth.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap("date", "")); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + private YearMonth read(final String json) throws IOException { + return READER.readValue(a2q(json)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java new file mode 100644 index 0000000000..64188e8f8e --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java @@ -0,0 +1,142 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.ZoneId; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.type.LogicalType; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZoneIdDeserTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + private final ObjectMapper MOCK_OBJECT_MIXIN_MAPPER = mapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .build(); + + @Test + public void testSimpleZoneIdDeser() + { + assertEquals(ZoneId.of("America/Chicago"), + MAPPER.readValue("\"America/Chicago\"", ZoneId.class)); + assertEquals(ZoneId.of("America/Anchorage"), + MAPPER.readValue("\"America/Anchorage\"", ZoneId.class)); + } + + @Test + public void testPolymorphicZoneIdDeser() + { + ObjectMapper mapper = JsonMapper.builder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .addModule(new JavaTimeModule()) + .build(); + ZoneId value = mapper.readValue("[\"" + ZoneId.class.getName() + "\",\"America/Denver\"]", ZoneId.class); + assertEquals(ZoneId.of("America/Denver"), value); + } + + @Test + public void testDeserialization01() + { + assertEquals(ZoneId.of("America/Chicago"), + MAPPER.readValue("\"America/Chicago\"", ZoneId.class)); + } + + @Test + public void testDeserialization02() + { + assertEquals(ZoneId.of("America/Anchorage"), + MAPPER.readValue("\"America/Anchorage\"", ZoneId.class)); + } + + @Test + public void testDeserializationWithTypeInfo02() + { + ZoneId value = MOCK_OBJECT_MIXIN_MAPPER.readValue("[\"" + ZoneId.class.getName() + "\",\"America/Denver\"]", ZoneId.class); + assertEquals(ZoneId.of("America/Denver"), value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() + { + + String key = "zoneId"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + ZoneId actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + ZoneId actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, + "empty string failed to deserialize to null with lenient setting"); + } + + public void testStrictDeserializeFromEmptyString() + { + + final String key = "zoneId"; + final ObjectMapper mapper = mapperBuilder() + .withCoercionConfig(LogicalType.DateTime, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail)) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + try { + objectReader.readValue(valueFromEmptyStr); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + verifyException(e, ZoneId.class.getName()); + } + } + + // [module-java8#68] + @Test + public void testZoneIdDeserFromEmpty() + { + // by default, should be fine + assertNull(MAPPER.readValue(q(" "), ZoneId.class)); + // but fail if coercion illegal + final ObjectMapper mapper = mapperBuilder() + .withCoercionConfig(LogicalType.DateTime, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail)) + .build(); + try { + mapper.readValue(q(" "), ZoneId.class); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + verifyException(e, ZoneId.class.getName()); + } + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZoneOffsetDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZoneOffsetDeserTest.java new file mode 100644 index 0000000000..3ce10d82a9 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZoneOffsetDeserTest.java @@ -0,0 +1,206 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.deser; + +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.CoercionAction; +import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.type.LogicalType; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZoneOffsetDeserTest extends ModuleTestBase +{ + private final static ObjectMapper MAPPER = newMapper(); + private final static ObjectReader READER = MAPPER.readerFor(ZoneOffset.class); + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + @Test + public void testSimpleZoneOffsetDeser() throws Exception + { + assertEquals(ZoneOffset.of("Z"), READER.readValue("\"Z\""), + "The value is not correct."); + assertEquals(ZoneOffset.of("+0300"), READER.readValue(q("+0300")), + "The value is not correct."); + assertEquals(ZoneOffset.of("-0630"), READER.readValue("\"-06:30\""), + "The value is not correct."); + } + + @Test + public void testPolymorphicZoneOffsetDeser() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .build(); + ZoneId value = mapper.readValue("[\"" + ZoneOffset.class.getName() + "\",\"+0415\"]", ZoneId.class); + assertTrue(value instanceof ZoneOffset); + assertEquals(ZoneOffset.of("+0415"), value); + } + + @Test + public void testDeserializationWithTypeInfo03() throws Exception + { + ObjectMapper mapper = mapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .build(); + ZoneId value = mapper.readValue("[\"" + ZoneOffset.class.getName() + "\",\"+0415\"]", ZoneId.class); + assertTrue(value instanceof ZoneOffset, "The value should be a ZoneOffset."); + assertEquals(ZoneOffset.of("+0415"), value, "The value is not correct."); + } + + @Test + public void testBadDeserializationAsString01() throws Throwable + { + try { + READER.readValue("\"notazonedoffset\""); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Invalid ID for ZoneOffset"); + } + } + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + READER.readValue("[\"+0300\"]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZoneOffset` from Array value"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + try { + READER.readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZoneOffset` from Array value"); + } + try { + READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZoneOffset` from Array value"); + } + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + ZoneOffset value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[\"+0300\"]"); + assertEquals(ZoneOffset.of("+0300"), value, "The value is not correct."); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + ZoneOffset value = READER + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "zoneOffset"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + ZoneId actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + ZoneId actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "zoneOffset"; + final ObjectMapper mapper = mapperBuilder() + .withCoercionConfig(LogicalType.DateTime, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail)) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + try { + objectReader.readValue(valueFromEmptyStr); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + verifyException(e, ZoneOffset.class.getName()); + } + } + + // [module-java8#68] + @Test + public void testZoneOffsetDeserFromEmpty() throws Exception + { + // by default, should be fine + assertNull(MAPPER.readValue(q(" "), ZoneOffset.class)); + // but fail if coercion illegal + final ObjectMapper mapper = mapperBuilder() + .withCoercionConfig(LogicalType.DateTime, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail)) + .build(); + try { + mapper.readerFor(ZoneOffset.class) + .readValue(q(" ")); + fail("Should not pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot coerce empty String"); + verifyException(e, ZoneOffset.class.getName()); + } + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java new file mode 100644 index 0000000000..0ce85b132e --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java @@ -0,0 +1,311 @@ +package tools.jackson.databind.datetime.deser; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Map; +import java.util.TimeZone; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Feature; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZonedDateTimeDeserTest extends ModuleTestBase +{ + private final ObjectReader READER = newMapper().readerFor(ZonedDateTime.class); + + private final ObjectReader READER_NON_NORMALIZED_ZONEID = JsonMapper.builder() + .addModule(new JavaTimeModule().disable(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) + .build() + .readerFor(ZonedDateTime.class); + + private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; + + static class WrapperWithFeatures { + @JsonFormat(without = JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + public ZonedDateTime value; + } + + static class WrapperWithReadTimestampsAsNanosDisabled { + @JsonFormat( + without=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public ZonedDateTime value; + + public WrapperWithReadTimestampsAsNanosDisabled() { } + public WrapperWithReadTimestampsAsNanosDisabled(ZonedDateTime v) { value = v; } + } + + static class WrapperWithReadTimestampsAsNanosEnabled { + @JsonFormat( + with=Feature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + public ZonedDateTime value; + + public WrapperWithReadTimestampsAsNanosEnabled() { } + public WrapperWithReadTimestampsAsNanosEnabled(ZonedDateTime v) { value = v; } + } + + @Test + public void testDeserFromString() throws Exception + { + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC), + READER.readValue(q("2000-01-01T12:00Z")), + "The value is not correct."); + } + + // [modules-java#281] + @Test + public void testDeserFromStringNoZoneIdNormalization() throws Exception + { + // 11-Nov-2023, tatu: Not sure this is great test but... does show diff + // behavior with and without `JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID` + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0, 0, TimeZone.getTimeZone("UTC").toZoneId()), + READER_NON_NORMALIZED_ZONEID.readValue(q("2000-01-01T12:00Z")), + "The value is not correct."); + } + + @Test + public void testDeserializationAsInt01() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosDisabled.class); + ZonedDateTime date = ZonedDateTime.of( + LocalDateTime.ofEpochSecond(1, 1000000, ZoneOffset.UTC), + ZoneOffset.UTC); + WrapperWithReadTimestampsAsNanosDisabled actual = + reader.readValue(a2q("{'value':1001}")); + assertEquals(date, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationAsInt02() throws Exception + { + ObjectReader reader = newMapper().readerFor(WrapperWithReadTimestampsAsNanosEnabled.class); + ZonedDateTime date = ZonedDateTime.of( + LocalDateTime.ofEpochSecond(1, 0, ZoneOffset.UTC), + ZoneOffset.UTC); + WrapperWithReadTimestampsAsNanosEnabled actual = + reader.readValue(a2q("{'value':1}")); + assertEquals(date, actual.value, "The value is not correct."); + } + + @Test + public void testDeserializationComparedToStandard() throws Throwable + { + String inputString = "2021-02-01T19:49:04.0513486Z"; + + assertEquals(DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(inputString, ZonedDateTime::from), + READER.readValue(q(inputString)), + "The value is not correct."); + } + + @Test + public void testDeserializationComparedToStandard2() throws Throwable + { + String inputString = "2021-02-01T19:49:04.0513486Z[UTC]"; + + ZonedDateTime converted = newMapperBuilder() + .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false) + .build() + .readerFor(ZonedDateTime.class).readValue(q(inputString)); + + assertEquals(DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(inputString, ZonedDateTime::from), + converted, + "The value is not correct."); + } + + @Test + public void testBadDeserializationAsString01() throws Throwable + { + try { + READER.readValue(q("notazone")); + fail("Should nae pass"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZonedDateTime` from String"); + } + } + + @Test + public void testDeserializationAsArrayDisabled() throws Throwable + { + try { + READER.readValue("[\"2000-01-01T12:00Z\"]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZonedDateTime` from Array"); + } + } + + @Test + public void testDeserializationAsEmptyArrayDisabled() throws Throwable + { + try { + READER.readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZonedDateTime` from Array"); + } + try { + newMapper() + .readerFor(ZonedDateTime.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[]"); + fail("expected MismatchedInputException"); + } catch (MismatchedInputException e) { + verifyException(e, "Cannot deserialize value of type `java.time.ZonedDateTime` from Array"); + } + } + + @Test + public void testDeserializationAsArrayEnabled() throws Throwable + { + ZonedDateTime value = newMapper() + .readerFor(ZonedDateTime.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) + .readValue("[\"2000-01-01T12:00Z\"]"); + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC), + value, + "The value is not correct."); + } + + @Test + public void testDeserializationAsEmptyArrayEnabled() throws Throwable + { + ZonedDateTime value = newMapper() + .readerFor(ZonedDateTime.class) + .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, + DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .readValue("[]"); + assertNull(value); + } + + @Test + public void testDeserializationWithZonePreserved() throws Throwable + { + WrapperWithFeatures wrapper = newMapper() + .readerFor(WrapperWithFeatures.class) + .readValue("{\"value\":\"2000-01-01T12:00+01:00\"}"); + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0 ,0, ZoneOffset.ofHours(1)), + wrapper.value, + "Timezone should be preserved."); + } + + /* + /********************************************************** + /* Tests for empty string handling + /********************************************************** + */ + + @Test + public void testLenientDeserializeFromEmptyString() throws Exception { + + String key = "zoneDateTime"; + ObjectMapper mapper = newMapper(); + ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + ZonedDateTime actualDateFromNullStr = actualMapFromNullStr.get(key); + assertNull(actualDateFromNullStr); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + Map actualMapFromEmptyStr = objectReader.readValue(valueFromEmptyStr); + ZonedDateTime actualDateFromEmptyStr = actualMapFromEmptyStr.get(key); + assertEquals(null, actualDateFromEmptyStr, "empty string failed to deserialize to null with lenient setting"); + } + + @Test + public void testStrictDeserializeFromEmptyString() throws Exception { + + final String key = "zonedDateTime"; + final ObjectMapper mapper = mapperBuilder() + .withConfigOverride(ZonedDateTime.class, + o -> o.setFormat(JsonFormat.Value.forLeniency(false))) + .build(); + final ObjectReader objectReader = mapper.readerFor(MAP_TYPE_REF); + + String valueFromNullStr = mapper.writeValueAsString(asMap(key, null)); + Map actualMapFromNullStr = objectReader.readValue(valueFromNullStr); + assertNull(actualMapFromNullStr.get(key)); + + String valueFromEmptyStr = mapper.writeValueAsString(asMap(key, "")); + assertThrows(MismatchedInputException.class, () -> objectReader.readValue(valueFromEmptyStr)); + } + + /* + /********************************************************** + /* Tests for ISO-8601 ZonedDateTimes that are colonless + /********************************************************** + */ + + @Test + public void testDeserializationWithoutColonInOffset() throws Throwable + { + WrapperWithFeatures wrapper = READER + .forType(WrapperWithFeatures.class) + .readValue("{\"value\":\"2000-01-01T12:00+0100\"}"); + + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0 ,0, ZoneOffset.ofHours(1)), + wrapper.value, + "Value parses as if it were with colon"); + } + + @Test + public void testDeserializationWithoutColonInTimeZoneWithTZDB() throws Throwable + { + WrapperWithFeatures wrapper = READER + .forType(WrapperWithFeatures.class) + .readValue("{\"value\":\"2000-01-01T12:00+0100[Europe/Paris]\"}"); + assertEquals(ZonedDateTime.of(2000, 1, 1, 12, 0, 0 ,0, ZoneId.of("Europe/Paris")), + wrapper.value, + "Timezone should be preserved."); + } + + @Test + public void ZonedDateTime_with_offset_can_be_deserialized() throws Exception { + ObjectReader r = newMapper().readerFor(ZonedDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); + + String base = "2015-07-24T12:23:34.184"; + for (String offset : Arrays.asList("+00", "-00")) { + String time = base + offset; + if (!System.getProperty("java.version").startsWith("1.8")) { + // JDK 8 cannot parse hour offsets without minutes + assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + '"')); + } + assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + "00" + '"')); + assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184Z"), r.readValue('"' + time + ":00" + '"')); + assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30" ), r.readValue('"' + time + "30" + '"')); + assertEquals(ZonedDateTime.parse("2015-07-24T12:23:34.184" + offset + ":30" ), r.readValue('"' + time + ":30" + '"')); + } + + for (String prefix : Arrays.asList("-", "+")) { + for (String hours : Arrays.asList("00", "01", "02", "03", "11", "12")) { + String time = base + prefix + hours; + ZonedDateTime expectedHour = ZonedDateTime.parse(time + ":00"); + if (!System.getProperty("java.version").startsWith("1.8")) { + // JDK 8 cannot parse hour offsets without minutes + assertEquals(expectedHour, r.readValue('"' + time + '"')); + } + assertEquals(expectedHour, r.readValue('"' + time + "00" + '"')); + assertEquals(expectedHour, r.readValue('"' + time + ":00" + '"')); + assertEquals(ZonedDateTime.parse(time + ":30"), r.readValue('"' + time + "30" + '"')); + assertEquals(ZonedDateTime.parse(time + ":30"), r.readValue('"' + time + ":30" + '"')); + } + } + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java b/src/test/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java new file mode 100644 index 0000000000..6580c40ffc --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java @@ -0,0 +1,59 @@ +package tools.jackson.databind.datetime.deser.key; + +import java.time.ZonedDateTime; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// for [modules-java8#306] +public class ZonedDateTimeKeyDeserializerTest + extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final TypeReference> MAP_TYPE_REF + = new TypeReference>() {}; + + @Test + public void Instant_style_can_be_deserialized() throws Exception { + Map map = MAPPER.readValue(getMap("2015-07-24T12:23:34.184Z"), + MAP_TYPE_REF); + Map.Entry entry = map.entrySet().iterator().next(); + assertEquals("2015-07-24T12:23:34.184Z", entry.getKey().toString()); + } + + @Test + public void ZonedDateTime_with_zone_name_can_be_deserialized() throws Exception { + Map map = MAPPER.readValue(getMap("2015-07-24T12:23:34.184Z[UTC]"), + MAP_TYPE_REF); + Map.Entry entry = map.entrySet().iterator().next(); + assertEquals("2015-07-24T12:23:34.184Z[UTC]", entry.getKey().toString()); + } + + // NOTE: Java 9+ test + @Test + public void ZonedDateTime_with_place_name_can_be_deserialized() throws Exception { + Map map = MAPPER.readValue(getMap("2015-07-24T12:23:34.184Z[Europe/London]"), + MAP_TYPE_REF); + Map.Entry entry = map.entrySet().iterator().next(); + assertEquals("2015-07-24T13:23:34.184+01:00[Europe/London]", entry.getKey().toString()); + } + + @Test + public void ZonedDateTime_with_offset_can_be_deserialized() throws Exception { + Map map = MAPPER.readValue(getMap("2015-07-24T12:23:34.184+02:00"), + MAP_TYPE_REF); + Map.Entry entry = map.entrySet().iterator().next(); + assertEquals("2015-07-24T12:23:34.184+02:00", entry.getKey().toString()); + } + + private static String getMap(String input) { + return "{\"" + input + "\": \"This is a string\"}"; + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/DurationAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/DurationAsKeyTest.java new file mode 100644 index 0000000000..4812dc59da --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/DurationAsKeyTest.java @@ -0,0 +1,34 @@ +package tools.jackson.databind.datetime.key; + +import java.time.Duration; +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DurationAsKeyTest extends ModuleTestBase +{ + private static final Duration DURATION = Duration.ofMinutes(13).plusSeconds(37).plusNanos(120 * 1000 * 1000L); + private static final String DURATION_STRING = "PT13M37.12S"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { }); + + @Test + public void testSerialization() throws Exception { + assertEquals(mapAsString(DURATION_STRING, "test"), + MAPPER.writeValueAsString(Collections.singletonMap(DURATION, "test"))); + } + + @Test + public void testDeserialization() throws Exception { + assertEquals(Collections.singletonMap(DURATION, "test"), READER.readValue(mapAsString(DURATION_STRING, "test"))); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/InstantAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/InstantAsKeyTest.java new file mode 100644 index 0000000000..70a049087f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/InstantAsKeyTest.java @@ -0,0 +1,50 @@ +package tools.jackson.databind.datetime.key; + +import java.time.Instant; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class InstantAsKeyTest extends ModuleTestBase +{ + private static final Instant INSTANT_0 = Instant.ofEpochMilli(0); + private static final String INSTANT_0_STRING = "1970-01-01T00:00:00Z"; + private static final Instant INSTANT = Instant.ofEpochSecond(1426325213l, 590000000l); + private static final String INSTANT_STRING = "2015-03-14T09:26:53.590Z"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { }); + + @Test + public void testSerialization0() throws Exception { + String value = MAPPER.writeValueAsString(asMap(INSTANT_0, "test")); + assertEquals(mapAsString(INSTANT_0_STRING, "test"), value); + } + + @Test + public void testSerialization1() throws Exception { + String value = MAPPER.writeValueAsString(asMap(INSTANT, "test")); + assertEquals(mapAsString(INSTANT_STRING, "test"), value); + } + + @Test + public void testDeserialization0() throws Exception { + Map value = READER.readValue(mapAsString(INSTANT_0_STRING, "test")); + Map EXP = asMap(INSTANT_0, "test"); + assertEquals(EXP, value, "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + Map value = READER.readValue(mapAsString(INSTANT_STRING, "test")); + Map EXP = asMap(INSTANT, "test"); + assertEquals(EXP, value, "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/LocalDateAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/LocalDateAsKeyTest.java new file mode 100644 index 0000000000..7a034e2f50 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/LocalDateAsKeyTest.java @@ -0,0 +1,33 @@ +package tools.jackson.databind.datetime.key; + +import java.time.LocalDate; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateAsKeyTest extends ModuleTestBase +{ + private static final LocalDate DATE = LocalDate.of(2015, 3, 14); + private static final String DATE_STRING = "2015-03-14"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { }); + + @Test + public void testSerialization() throws Exception { + assertEquals(mapAsString(DATE_STRING, "test"), + MAPPER.writeValueAsString(asMap(DATE, "test"))); + } + + @Test + public void testDeserialization() throws Exception { + assertEquals(asMap(DATE, "test"), READER.readValue(mapAsString(DATE_STRING, "test"))); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/LocalDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/LocalDateTimeAsKeyTest.java new file mode 100644 index 0000000000..0366141e7a --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/LocalDateTimeAsKeyTest.java @@ -0,0 +1,80 @@ +package tools.jackson.databind.datetime.key; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.deser.DeserializationProblemHandler; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateTimeAsKeyTest extends ModuleTestBase +{ + private static final LocalDateTime DATE_TIME_0 = LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC); + /* + * Current serializer is LocalDateTime.toString(), which omits seconds if it can + */ + private static final String DATE_TIME_0_STRING = "1970-01-01T00:00"; + private static final LocalDateTime DATE_TIME = LocalDateTime.of(2015, 3, 14, 9, 26, 53, 590 * 1000 * 1000); + private static final String DATE_TIME_STRING = "2015-03-14T09:26:53.590"; + + private final TypeReference> TYPE_REF = new TypeReference>() { }; + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + String value = MAPPER.writeValueAsString(asMap(DATE_TIME_0, "test")); + assertEquals(mapAsString(DATE_TIME_0_STRING, "test"), value); + } + + @Test + public void testSerialization1() throws Exception { + String value = MAPPER.writeValueAsString(asMap(DATE_TIME, "test")); + assertEquals(mapAsString(DATE_TIME_STRING, "test"), value); + } + + @Test + public void testDeserialization0() throws Exception { + Map value = READER.readValue( + mapAsString(DATE_TIME_0_STRING, "test")); + assertEquals(asMap(DATE_TIME_0, "test"), value, "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + Map value = READER.readValue( + mapAsString(DATE_TIME_STRING, "test")); + assertEquals(asMap(DATE_TIME, "test"), value, "Value is incorrect"); + } + + @Test + public void testDateTimeExceptionIsHandled() throws Throwable + { + LocalDateTime now = LocalDateTime.now(); + DeserializationProblemHandler handler = new DeserializationProblemHandler() { + @Override + public Object handleWeirdKey(DeserializationContext ctxt, Class targetType, + String valueToConvert, String failureMsg) { + if (LocalDateTime.class == targetType) { + if ("now".equals(valueToConvert)) { + return now; + } + } + return NOT_HANDLED; + } + }; + Map value = mapperBuilder().addHandler(handler) + .build() + .readValue(mapAsString("now", "test"), TYPE_REF); + + assertEquals(asMap(now, "test"), value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/LocalTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/LocalTimeAsKeyTest.java new file mode 100644 index 0000000000..c6b89f3092 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/LocalTimeAsKeyTest.java @@ -0,0 +1,53 @@ +package tools.jackson.databind.datetime.key; + +import java.time.LocalTime; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalTimeAsKeyTest extends ModuleTestBase +{ + private static final LocalTime TIME_0 = LocalTime.ofSecondOfDay(0); + /* + * Seconds are omitted if possible + */ + private static final String TIME_0_STRING = "00:00"; + private static final LocalTime TIME = LocalTime.of(3, 14, 15, 920 * 1000 * 1000); + private static final String TIME_STRING = "03:14:15.920"; + + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + assertEquals(mapAsString(TIME_0_STRING, "test"), + MAPPER.writeValueAsString(asMap(TIME_0, "test"))); + } + + @Test + public void testSerialization1() throws Exception { + assertEquals(mapAsString(TIME_STRING, "test"), + MAPPER.writeValueAsString(asMap(TIME, "test"))); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(TIME_0, "test"), READER.readValue(mapAsString(TIME_0_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(TIME, "test"), READER.readValue(mapAsString(TIME_STRING, "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/MonthDayAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/MonthDayAsKeyTest.java new file mode 100644 index 0000000000..e42d695dbf --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/MonthDayAsKeyTest.java @@ -0,0 +1,36 @@ +package tools.jackson.databind.datetime.key; + +import java.time.MonthDay; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class MonthDayAsKeyTest extends ModuleTestBase +{ + private static final MonthDay MONTH_DAY = MonthDay.of(3, 14); + private static final String MONTH_DAY_STRING = "--03-14"; + + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization() throws Exception { + assertEquals(mapAsString(MONTH_DAY_STRING, "test"), MAPPER.writeValueAsString(asMap(MONTH_DAY, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization() throws Exception { + assertEquals(asMap(MONTH_DAY, "test"), READER.readValue(mapAsString(MONTH_DAY_STRING, "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/OffsetDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/OffsetDateTimeAsKeyTest.java new file mode 100644 index 0000000000..ca9749c55f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/OffsetDateTimeAsKeyTest.java @@ -0,0 +1,70 @@ +package tools.jackson.databind.datetime.key; + +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetDateTimeAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private static final OffsetDateTime DATE_TIME_0 = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneOffset.UTC); + private static final String DATE_TIME_0_STRING = "1970-01-01T00:00Z"; + private static final OffsetDateTime DATE_TIME_1 = OffsetDateTime.of(2015, 3, 14, 9, 26, 53, 590 * 1000 * 1000, ZoneOffset.UTC); + private static final String DATE_TIME_1_STRING = "2015-03-14T09:26:53.590Z"; + private static final OffsetDateTime DATE_TIME_2 = OffsetDateTime.of(2015, 3, 14, 9, 26, 53, 590 * 1000 * 1000, ZoneOffset.ofHours(6)); + private static final String DATE_TIME_2_STRING = "2015-03-14T09:26:53.590+06:00"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + String value = MAPPER.writeValueAsString(asMap(DATE_TIME_0, "test")); + assertEquals(mapAsString(DATE_TIME_0_STRING, "test"), value, + "Value is incorrect"); + } + + @Test + public void testSerialization1() throws Exception { + assertEquals(mapAsString(DATE_TIME_1_STRING, "test"), + MAPPER.writeValueAsString(asMap(DATE_TIME_1, "test")), + "Value is incorrect"); + } + + @Test + public void testSerialization2() throws Exception { + assertEquals(mapAsString(DATE_TIME_2_STRING, "test"), + MAPPER.writeValueAsString(asMap(DATE_TIME_2, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(DATE_TIME_0, "test"), READER.readValue(mapAsString(DATE_TIME_0_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(DATE_TIME_1, "test"), READER.readValue(mapAsString(DATE_TIME_1_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization2() throws Exception { + assertEquals(asMap(DATE_TIME_2, "test"), + READER.readValue(mapAsString(DATE_TIME_2_STRING, "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/OffsetTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/OffsetTimeAsKeyTest.java new file mode 100644 index 0000000000..080411e394 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/OffsetTimeAsKeyTest.java @@ -0,0 +1,70 @@ +package tools.jackson.databind.datetime.key; + +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetTimeAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private static final OffsetTime TIME_0 = OffsetTime.of(0, 0, 0, 0, ZoneOffset.UTC); + private static final String TIME_0_STRING = "00:00Z"; + private static final OffsetTime TIME_1 = OffsetTime.of(3, 14, 15, 920 * 1000 * 1000, ZoneOffset.UTC); + private static final String TIME_1_STRING = "03:14:15.920Z"; + private static final OffsetTime TIME_2 = OffsetTime.of(3, 14, 15, 920 * 1000 * 1000, ZoneOffset.ofHours(6)); + private static final String TIME_2_STRING = "03:14:15.920+06:00"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + assertEquals(mapAsString(TIME_0_STRING, "test"), + MAPPER.writeValueAsString(asMap(TIME_0, "test"))); + } + + @Test + public void testSerialization1() throws Exception { + assertEquals(mapAsString(TIME_1_STRING, "test"), + MAPPER.writeValueAsString(asMap(TIME_1, "test")), + "Value is incorrect"); + } + + @Test + public void testSerialization2() throws Exception { + assertEquals(mapAsString(TIME_2_STRING, "test"), + MAPPER.writeValueAsString(asMap(TIME_2, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(TIME_0, "test"), + READER.readValue(mapAsString(TIME_0_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(TIME_1, "test"), + READER.readValue(mapAsString(TIME_1_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization2() throws Exception { + assertEquals(asMap(TIME_2, "test"), + READER.readValue(mapAsString(TIME_2_STRING, "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/PeriodAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/PeriodAsKeyTest.java new file mode 100644 index 0000000000..5e338b4d48 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/PeriodAsKeyTest.java @@ -0,0 +1,54 @@ +package tools.jackson.databind.datetime.key; + +import java.time.Period; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class PeriodAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private static final Period PERIOD_0 = Period.of(0, 0, 0); + private static final String PERIOD_0_STRING = "P0D"; + private static final Period PERIOD = Period.of(3, 1, 4); + private static final String PERIOD_STRING = "P3Y1M4D"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + assertEquals(mapAsString(PERIOD_0_STRING, "test"), + MAPPER.writeValueAsString(asMap(PERIOD_0, "test")), + "Value is incorrect"); + } + + @Test + public void testSerialization1() throws Exception { + assertEquals(mapAsString(PERIOD_STRING, "test"), + MAPPER.writeValueAsString(asMap(PERIOD, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(PERIOD_0, "test"), + READER.readValue(mapAsString(PERIOD_0_STRING, "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(PERIOD, "test"), + READER.readValue(mapAsString(PERIOD_STRING, "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/YearAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/YearAsKeyTest.java new file mode 100644 index 0000000000..ef61a3a370 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/YearAsKeyTest.java @@ -0,0 +1,70 @@ +package tools.jackson.databind.datetime.key; + +import java.time.Year; +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.exc.InvalidFormatException; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testKeySerialization() throws Exception { + assertEquals(mapAsString("3141", "test"), + MAPPER.writeValueAsString(asMap(Year.of(3141), "test")), + "Value is incorrect"); + } + + @Test + public void testKeyDeserialization() throws Exception { + assertEquals(asMap(Year.of(3141), "test"), READER.readValue(mapAsString("3141", "test")), + "Value is incorrect"); + // Test both padded, unpadded + assertEquals(asMap(Year.of(476), "test"), READER.readValue(mapAsString("0476", "test")), + "Value is incorrect"); + assertEquals(asMap(Year.of(476), "test"), READER.readValue(mapAsString("476", "test")), + "Value is incorrect"); + } + + @Test + public void deserializeYearKey_notANumber() throws Exception { + assertThrows(InvalidFormatException.class, () -> { + READER.readValue(mapAsString("10000BC", "test")); + }); + } + + @Test + public void deserializeYearKey_notAYear() throws Exception { + assertThrows(InvalidFormatException.class, () -> { + READER.readValue(mapAsString(Integer.toString(Year.MAX_VALUE+1), "test")); + }); + } + + @Test + public void serializeAndDeserializeYearKeyUnpadded() throws Exception { + // fix for issue #51 verify we can deserialize an unpadded year e.g. "1" + Map testMap = Collections.singletonMap(Year.of(1), 1F); + String serialized = MAPPER.writeValueAsString(testMap); + TypeReference> yearFloatTypeReference = new TypeReference>() {}; + Map deserialized = MAPPER.readValue(serialized, yearFloatTypeReference); + assertEquals(testMap, deserialized); + + // actually, check padded as well just to make sure + Map deserialized2 = MAPPER.readValue(a2q("{'0001':1.0}"), + yearFloatTypeReference); + assertEquals(testMap, deserialized2); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/YearMonthAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/YearMonthAsKeyTest.java new file mode 100644 index 0000000000..a0478ecf31 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/YearMonthAsKeyTest.java @@ -0,0 +1,34 @@ +package tools.jackson.databind.datetime.key; + +import java.time.YearMonth; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearMonthAsKeyTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { + }); + + @Test + public void testSerialization() throws Exception { + assertEquals(mapAsString("3141-05", "test"), + MAPPER.writeValueAsString(asMap(YearMonth.of(3141, 5), "test")), + "Value is incorrect"); + } + + @Test + public void testDeserialization() throws Exception { + assertEquals(asMap(YearMonth.of(3141, 5), "test"), + READER.readValue(mapAsString("3141-05", "test")), + "Value is incorrect"); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/ZoneIdAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/ZoneIdAsKeyTest.java new file mode 100644 index 0000000000..57c7752060 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/ZoneIdAsKeyTest.java @@ -0,0 +1,62 @@ +package tools.jackson.databind.datetime.key; + +import java.time.ZoneId; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ZoneIdAsKeyTest extends ModuleTestBase +{ + private static final ZoneId ZONE_0 = ZoneId.of("UTC"); + private static final String ZONE_0_STRING = "UTC"; + private static final ZoneId ZONE_1 = ZoneId.of("+06:00"); + private static final String ZONE_1_STRING = "+06:00"; + private static final ZoneId ZONE_2 = ZoneId.of("Europe/London"); + private static final String ZONE_2_STRING = "Europe/London"; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { }); + + @Test + public void testSerialization0() throws Exception { + assertEquals(mapAsString(ZONE_0_STRING, "test"), + MAPPER.writeValueAsString(asMap(ZONE_0, "test"))); + } + + @Test + public void testSerialization1() throws Exception { + assertEquals(mapAsString(ZONE_1_STRING, "test"), + MAPPER.writeValueAsString(asMap(ZONE_1, "test"))); + } + + @Test + public void testSerialization2() throws Exception { + assertEquals(mapAsString(ZONE_2_STRING, "test"), + MAPPER.writeValueAsString(asMap(ZONE_2, "test"))); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(ZONE_0, "test"), + READER.readValue(mapAsString(ZONE_0_STRING, "test"))); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(ZONE_1, "test"), + READER.readValue(mapAsString(ZONE_1_STRING, "test"))); + } + + @Test + public void testDeserialization2() throws Exception { + assertEquals(asMap(ZONE_2, "test"), + READER.readValue(mapAsString(ZONE_2_STRING, "test"))); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/ZoneOffsetAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/ZoneOffsetAsKeyTest.java new file mode 100644 index 0000000000..6e3f7bcaee --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/ZoneOffsetAsKeyTest.java @@ -0,0 +1,48 @@ +package tools.jackson.databind.datetime.key; + +import java.time.ZoneOffset; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ZoneOffsetAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private static final ZoneOffset OFFSET_0 = ZoneOffset.UTC; + private static final String OFFSET_0_STRING = "Z"; + private static final ZoneOffset OFFSET_1 = ZoneOffset.ofHours(6); + private static final String OFFSET_1_STRING = "+06:00"; + + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerialization0() throws Exception { + String value = MAPPER.writeValueAsString(asMap(OFFSET_0, "test")); + assertEquals(mapAsString(OFFSET_0_STRING, "test"), value); + } + + @Test + public void testSerialization1() throws Exception { + String value = MAPPER.writeValueAsString(asMap(OFFSET_1, "test")); + assertEquals(mapAsString(OFFSET_1_STRING, "test"), value); + } + + @Test + public void testDeserialization0() throws Exception { + Map value = MAPPER.readValue(mapAsString(OFFSET_0_STRING, "test"), TYPE_REF); + assertEquals(asMap(OFFSET_0, "test"), value); + } + + @Test + public void testDeserialization1() throws Exception { + Map value = MAPPER.readValue(mapAsString(OFFSET_1_STRING, "test"), TYPE_REF); + assertEquals(asMap(OFFSET_1, "test"), value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/key/ZonedDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/datetime/key/ZonedDateTimeAsKeyTest.java new file mode 100644 index 0000000000..606af5917d --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/key/ZonedDateTimeAsKeyTest.java @@ -0,0 +1,90 @@ +package tools.jackson.databind.datetime.key; + +import java.time.*; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZonedDateTimeAsKeyTest extends ModuleTestBase +{ + private static final TypeReference> TYPE_REF = new TypeReference>() { + }; + private static final ZonedDateTime DATE_TIME_0 = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneOffset.UTC); + private static final String DATE_TIME_0_STRING = "1970-01-01T00:00:00Z"; +// private static final Instant DATE_TIME_0_INSTANT = DATE_TIME_0.toInstant(); + + private static final ZonedDateTime DATE_TIME_1 = ZonedDateTime.of( + 2015, 3, 14, 9, 26, 53, 590 * 1000 * 1000, ZoneOffset.UTC); + private static final String DATE_TIME_1_STRING = "2015-03-14T09:26:53.59Z"; + + private static final ZonedDateTime DATE_TIME_2 = ZonedDateTime.of( + 2015, 3, 14, 9, 26, 53, 590 * 1000 * 1000, ZoneId.of("Europe/Budapest")); + /** + * Value of {@link #DATE_TIME_2} after it's been serialized and read back. Serialization throws away time zone information, it only + * keeps offset data. + */ + private static final ZonedDateTime DATE_TIME_2_OFFSET = DATE_TIME_2.withZoneSameInstant(ZoneOffset.ofHours(1)); + private static final String DATE_TIME_2_STRING = "2015-03-14T09:26:53.59+01:00";; + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(TYPE_REF); + + @Test + public void testSerialization0() throws Exception { + String value = MAPPER.writerFor(TYPE_REF).writeValueAsString(asMap(DATE_TIME_0, "test")); + assertEquals(mapAsString(DATE_TIME_0_STRING, "test"), value); + } + + @Test + public void testSerialization1() throws Exception { + String value = MAPPER.writerFor(TYPE_REF).writeValueAsString(asMap(DATE_TIME_1, "test")); + assertEquals(mapAsString(DATE_TIME_1_STRING, "test"), value); + } + + @Test + public void testSerialization2() throws Exception { + String value = MAPPER.writerFor(TYPE_REF).writeValueAsString(asMap(DATE_TIME_2, "test")); + assertEquals(mapAsString(DATE_TIME_2_STRING, "test"), value); + } + + @Test + public void testDeserialization0() throws Exception { + assertEquals(asMap(DATE_TIME_0, "test"), + READER.readValue(mapAsString(DATE_TIME_0_STRING, "test"))); + } + + @Test + public void testDeserialization1() throws Exception { + assertEquals(asMap(DATE_TIME_1, "test"), + READER.readValue(mapAsString(DATE_TIME_1_STRING, "test"))); + } + + @Test + public void testDeserialization2() throws Exception { + assertEquals(asMap(DATE_TIME_2_OFFSET, "test"), + READER.readValue(mapAsString(DATE_TIME_2_STRING, "test"))); + } + + @Test + public void testSerializationToInstantWithNanos() throws Exception { + String value = mapperBuilder().enable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS).build() + .writerFor(TYPE_REF).writeValueAsString(asMap(DATE_TIME_1, "test")); + assertEquals(mapAsString(String.valueOf(DATE_TIME_1.toEpochSecond()) + '.' + DATE_TIME_1.getNano(), "test"), value); + } + + @Test + public void testSerializationToInstantWithoutNanos() throws Exception { + String value = mapperBuilder().enable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS).build() + .writerFor(TYPE_REF).writeValueAsString(asMap(DATE_TIME_1, "test")); + assertEquals(mapAsString(String.valueOf(DATE_TIME_1.toInstant().toEpochMilli()), "test"), value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/misc/DateTimeExceptionTest.java b/src/test/java/tools/jackson/databind/datetime/misc/DateTimeExceptionTest.java new file mode 100644 index 0000000000..8e21d7b377 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/misc/DateTimeExceptionTest.java @@ -0,0 +1,24 @@ +package tools.jackson.databind.datetime.misc; + +import java.time.DateTimeException; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DateTimeExceptionTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + + // [modules-java#319]: should not fail to ser/deser DateTimeException + @Test + public void testDateTimeExceptionRoundtrip() throws Exception + { + String json = MAPPER.writeValueAsString(new DateTimeException("Test!")); + DateTimeException result = MAPPER.readValue(json, DateTimeException.class); + assertEquals("Test!", result.getMessage()); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/misc/DateTimeSchemasTest.java b/src/test/java/tools/jackson/databind/datetime/misc/DateTimeSchemasTest.java new file mode 100644 index 0000000000..049bda2dbd --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/misc/DateTimeSchemasTest.java @@ -0,0 +1,239 @@ +package tools.jackson.databind.datetime.misc; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.*; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.*; +import tools.jackson.databind.jsonFormatVisitors.*; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DateTimeSchemasTest extends ModuleTestBase +{ + static class VisitorWrapper implements JsonFormatVisitorWrapper { + SerializationContext serializationContext; + final String baseName; + final Map traversedProperties; + + public VisitorWrapper(SerializationContext ctxt, String baseName, Map traversedProperties) { + this.serializationContext = ctxt; + this.baseName = baseName; + this.traversedProperties = traversedProperties; + } + + VisitorWrapper createSubtraverser(String bn) { + return new VisitorWrapper(getContext(), bn, traversedProperties); + } + + public Map getTraversedProperties() { + return traversedProperties; + } + + @Override + public JsonObjectFormatVisitor expectObjectFormat(JavaType type) { + return new JsonObjectFormatVisitor.Base(serializationContext) { + @Override + public void property(BeanProperty prop) { + anyProperty(prop); + } + + @Override + public void optionalProperty(BeanProperty prop) { + anyProperty(prop); + } + + private void anyProperty(BeanProperty prop) { + final String propertyName = prop.getFullName().toString(); + traversedProperties.put(baseName + propertyName, ""); + serializationContext.findPrimaryPropertySerializer(prop.getType(), prop) + .acceptJsonFormatVisitor(createSubtraverser(baseName + propertyName + "."), prop.getType()); + } + }; + } + + @Override + public JsonArrayFormatVisitor expectArrayFormat(JavaType type) { + traversedProperties.put(baseName, "ARRAY/"+type.getGenericSignature()); + return null; + } + + @Override + public JsonStringFormatVisitor expectStringFormat(JavaType type) { + return new JsonStringFormatVisitor.Base() { + @Override + public void format(JsonValueFormat format) { + traversedProperties.put(baseName, "STRING/"+format.name()); + } + }; + } + + @Override + public JsonNumberFormatVisitor expectNumberFormat(JavaType type) { + return new JsonNumberFormatVisitor.Base() { + @Override + public void numberType(JsonParser.NumberType format) { + traversedProperties.put(baseName, "NUMBER/"+format.name()); + } + }; + } + + @Override + public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) { + return new JsonIntegerFormatVisitor.Base() { + @Override + public void numberType(JsonParser.NumberType numberType) { + traversedProperties.put(baseName + "numberType", "INTEGER/" + numberType.name()); + } + + @Override + public void format(JsonValueFormat format) { + traversedProperties.put(baseName + "format", "INTEGER/" + format.name()); + } + }; + } + + @Override + public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) { + traversedProperties.put(baseName, "BOOLEAN"); + return new JsonBooleanFormatVisitor.Base(); + } + + @Override + public JsonNullFormatVisitor expectNullFormat(JavaType type) { + return new JsonNullFormatVisitor.Base(); + } + + @Override + public JsonAnyFormatVisitor expectAnyFormat(JavaType type) { + traversedProperties.put(baseName, "ANY"); + return new JsonAnyFormatVisitor.Base(); + } + + @Override + public JsonMapFormatVisitor expectMapFormat(JavaType type) { + traversedProperties.put(baseName, "MAP"); + return new JsonMapFormatVisitor.Base(serializationContext); + } + + @Override + public SerializationContext getContext() { + return serializationContext; + } + + @Override + public void setContext(SerializationContext ctxt) { + this.serializationContext = ctxt; + } + } + + // 05-Feb-2025, tatu: Change defaults to Jackson 2.x wrt serialization + // shape (as Timestamps vs Strings) + private final ObjectMapper MAPPER = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + // // // Local date/time types + + // [modules-java8#105] + @Test + public void testLocalTimeSchema() throws Exception + { + VisitorWrapper wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().acceptJsonFormatVisitor(LocalTime.class, wrapper); + Map properties = wrapper.getTraversedProperties(); + + // By default, serialized as an int array, so: + assertEquals(1, properties.size()); + _verifyIntArrayType(properties.get("")); + + // but becomes date/time + wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .acceptJsonFormatVisitor(LocalTime.class, wrapper); + properties = wrapper.getTraversedProperties(); + _verifyTimeType(properties.get("")); + } + + @Test + public void testLocalDateSchema() throws Exception + { + VisitorWrapper wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().acceptJsonFormatVisitor(LocalDate.class, wrapper); + Map properties = wrapper.getTraversedProperties(); + + // By default, serialized as an int array, so: + assertEquals(1, properties.size()); + _verifyIntArrayType(properties.get("")); + + // but becomes date/time + wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .acceptJsonFormatVisitor(LocalDate.class, wrapper); + properties = wrapper.getTraversedProperties(); + _verifyDateType(properties.get("")); + } + + // // // Zoned date/time types + + @Test + public void testDateTimeSchema() throws Exception + { + VisitorWrapper wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().acceptJsonFormatVisitor(ZonedDateTime.class, wrapper); + Map properties = wrapper.getTraversedProperties(); + + // By default, serialized as an int array, so: + assertEquals(1, properties.size()); + _verifyBigDecimalType(properties.get("")); + + // but becomes long + wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer() + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .acceptJsonFormatVisitor(ZonedDateTime.class, wrapper); + properties = wrapper.getTraversedProperties(); + _verifyLongType(properties.get("numberType")); + _verifyLongFormat(properties.get("format")); + + // but becomes date/time + wrapper = new VisitorWrapper(null, "", new HashMap()); + MAPPER.writer().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .acceptJsonFormatVisitor(ZonedDateTime.class, wrapper); + properties = wrapper.getTraversedProperties(); + _verifyDateTimeType(properties.get("")); + } + + private void _verifyIntArrayType(String desc) { + assertEquals("ARRAY/Ljava/util/List;", desc); + } + + private void _verifyTimeType(String desc) { + assertEquals("STRING/TIME", desc); + } + + private void _verifyDateType(String desc) { + assertEquals("STRING/DATE", desc); + } + + private void _verifyDateTimeType(String desc) { + assertEquals("STRING/DATE_TIME", desc); + } + + private void _verifyBigDecimalType(String desc) { + assertEquals("NUMBER/BIG_DECIMAL", desc); + } + + private void _verifyLongType(String desc) { + assertEquals("INTEGER/LONG", desc); + } + + private void _verifyLongFormat(String desc) { + assertEquals("INTEGER/UTC_MILLISEC", desc); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/misc/DeductionTypeSerialization296Test.java b/src/test/java/tools/jackson/databind/datetime/misc/DeductionTypeSerialization296Test.java new file mode 100644 index 0000000000..c8beaecbdb --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/misc/DeductionTypeSerialization296Test.java @@ -0,0 +1,87 @@ +package tools.jackson.databind.datetime.misc; + +import java.time.*; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; + +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// for [modules-java8#296]: problem with `JsonTypeInfo.Id.DEDUCTION` +public class DeductionTypeSerialization296Test extends ModuleTestBase +{ + static class Wrapper { + @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) + public Object value; + + public Wrapper(Object value) { + this.value = value; + } + } + + private final ObjectMapper MAPPER = mapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + @Test + public void testLocalDate() throws Exception + { + LocalDate date = LocalDate.of(1986, Month.JANUARY, 17); + assertEquals(a2q("{'value':'1986-01-17'}"), + MAPPER.writeValueAsString(new Wrapper(date))); + } + + @Test + public void testLocalDateTime() throws Exception + { + LocalDateTime datetime = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); + assertEquals(a2q("{'value':'2013-08-21T09:22:00.000000057'}"), + MAPPER.writeValueAsString(new Wrapper(datetime))); + } + + @Test + public void testLocalTime() throws Exception + { + LocalTime time = LocalTime.of(9, 22, 57); + assertEquals(a2q("{'value':'09:22:57'}"), + MAPPER.writeValueAsString(new Wrapper(time))); + } + + @Test + public void testMonthDate() throws Exception + { + MonthDay date = MonthDay.of(Month.JANUARY, 17); + assertEquals(a2q("{'value':'--01-17'}"), + MAPPER.writeValueAsString(new Wrapper(date))); + } + + @Test + public void testOffsetTime() throws Exception + { + OffsetTime time = OffsetTime.of(15, 43, 0, 0, ZoneOffset.of("+0300")); + assertEquals(a2q("{'value':'15:43+03:00'}"), + MAPPER.writeValueAsString(new Wrapper(time))); + } + + @Test + public void testYearMonth() throws Exception + { + YearMonth date = YearMonth.of(1986, Month.JANUARY); + assertEquals(a2q("{'value':'1986-01'}"), + MAPPER.writeValueAsString(new Wrapper(date))); + } + + @Test + public void testZoneId() throws Exception + { + ZoneId zone = ZoneId.of("America/Denver"); + assertEquals(a2q("{'value':'America/Denver'}"), + MAPPER.writeValueAsString(new Wrapper(zone))); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/misc/JDKSerializabilityTest.java b/src/test/java/tools/jackson/databind/datetime/misc/JDKSerializabilityTest.java new file mode 100644 index 0000000000..2fad3a3b61 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/misc/JDKSerializabilityTest.java @@ -0,0 +1,46 @@ +package tools.jackson.databind.datetime.misc; + +import java.io.*; +import java.time.Year; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.*; +import tools.jackson.databind.datetime.*; + +import static org.junit.jupiter.api.Assertions.*; + +public class JDKSerializabilityTest extends ModuleTestBase +{ + @Test + public void testJDKSerializability() throws Exception { + final Year input = Year.of(1986); + ObjectMapper mapper = newMapper(); + String json1 = mapper.writeValueAsString(input); + + // validate we can still use it to deserialize jackson objects + ObjectMapper thawedMapper = serializeAndDeserialize(mapper); + String json2 = thawedMapper.writeValueAsString(input); + + assertEquals(json1, json2); + + Year result = thawedMapper.readValue(json1, Year.class); + assertEquals(input, result); + } + + private ObjectMapper serializeAndDeserialize(ObjectMapper mapper) throws Exception { + //verify serialization + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream); + + outputStream.writeObject(mapper); + byte[] serializedBytes = byteArrayOutputStream.toByteArray(); + + //verify deserialization + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedBytes); + ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream); + + Object deserializedObject = inputStream.readObject(); + return (ObjectMapper) deserializedObject; + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/misc/UnsupportedTypesTest.java b/src/test/java/tools/jackson/databind/datetime/misc/UnsupportedTypesTest.java new file mode 100644 index 0000000000..63195d0e16 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/misc/UnsupportedTypesTest.java @@ -0,0 +1,34 @@ +package tools.jackson.databind.datetime.misc; + +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalAdjusters; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class UnsupportedTypesTest extends ModuleTestBase +{ + // [modules-java8#207] + static class TAWrapper { + public TemporalAdjuster a; + + public TAWrapper(TemporalAdjuster a) { + this.a = a; + } + } + + // [modules-java#207]: should not fail on `TemporalAdjuster` + @Test + public void testTemporalAdjusterSerialization() throws Exception + { + ObjectMapper mapper = newMapper(); + + // Not 100% sure how this happens, actually; should fail on empty "POJO"? + assertEquals(a2q("{'a':{}}"), + mapper.writeValueAsString(new TAWrapper(TemporalAdjusters.firstDayOfMonth()))); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/DurationSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/DurationSerTest.java new file mode 100644 index 0000000000..6c2760e41b --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/DurationSerTest.java @@ -0,0 +1,322 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Duration; +import java.time.temporal.TemporalAmount; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectWriter; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DurationSerTest extends ModuleTestBase +{ + private final ObjectWriter WRITER = newMapper().writer(); + + // [datetime#224] + static class MyDto224 { + @JsonFormat(pattern = "MINUTES" + // Work-around from issue: +// , without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS + ) + @JsonProperty("mins") + final Duration duration; + + public MyDto224(Duration d) { duration = d; } + + public Duration getDuration() { return duration; } + } + + // [datetime#282] + static class Bean282 { + @JsonFormat(pattern = "SECONDS") + public Duration duration; + + public Bean282(Duration d) { duration = d; } + } + + @Test + public void testSerializationAsTimestampNanoseconds01() throws Exception + { + Duration duration = Duration.ofSeconds(60L, 0); + String value = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(duration); + assertEquals("60"+NO_NANOSECS_SUFFIX, value); + } + + @Test + public void testSerializationAsTimestampNanoseconds02() throws Exception + { + Duration duration = Duration.ofSeconds(13498L, 8374); + String value = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(duration); + assertEquals("13498.000008374", value); + } + + // [modules-java8#165] + @Test + public void testSerializationAsTimestampNanoseconds03() throws Exception + { + ObjectWriter w = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); + + // 20-Oct-2020, tatu: Very weird, but "use nanoseconds" actually results + // in unit being seconds, with fractions (with nanosec precision) + String value = w.writeValueAsString(Duration.ofMillis(1L)); + assertEquals("0.001000000", value); + + value = w.writeValueAsString(Duration.ofMillis(-1L)); + assertEquals("-0.001000000", value); + } + + @Test + public void testSerializationAsTimestampMilliseconds01() throws Exception + { + final ObjectWriter w = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); + String value = w.writeValueAsString(Duration.ofSeconds(45L, 0)); + assertEquals("45000", value); + + // and with negative value too + value = w.writeValueAsString(Duration.ofSeconds(-32L, 0)); + assertEquals("-32000", value); + } + + @Test + public void testSerializationAsTimestampMilliseconds02() throws Exception + { + String value = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(Duration.ofSeconds(13498L, 8374)); + assertEquals("13498000", value); + } + + @Test + public void testSerializationAsTimestampMilliseconds03() throws Exception + { + Duration duration = Duration.ofSeconds(13498L, 837481723); + String value = WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(duration); + assertEquals("13498837", value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + Duration duration = Duration.ofSeconds(60L, 0); + String value = WRITER + .without(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .writeValueAsString(duration); + assertEquals(q(duration.toString()), value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + Duration duration = Duration.ofSeconds(13498L, 8374); + String value = WRITER + .without(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .writeValueAsString(duration); + assertEquals(q(duration.toString()), value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, + SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .build(); + Duration duration = Duration.ofSeconds(13498L, 8374); + String value = mapper.writeValueAsString(duration); + + assertEquals("[\"" + Duration.class.getName() + "\",13498.000008374]", value); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .build(); + Duration duration = Duration.ofSeconds(13498L, 837481723); + String value = mapper.writeValueAsString(duration); + + assertEquals("[\"" + Duration.class.getName() + "\",13498837]", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .build(); + Duration duration = Duration.ofSeconds(13498L, 8374); + String value = mapper.writeValueAsString(duration); + + assertEquals("[\"" + Duration.class.getName() + "\",\"" + duration.toString() + "\"]", value); + } + + /* + /********************************************************** + /* Tests for custom patterns (modules-java8#189) + /********************************************************** + */ + + @Test + public void shouldSerializeInNanos_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("NANOS"); + assertEquals("3600000000000", mapper.writeValueAsString(Duration.ofHours(1))); + } + + @Test + public void shouldSerializeInMicros_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MICROS"); + assertEquals("1000", mapper.writeValueAsString(Duration.ofMillis(1))); + } + + @Test + public void shouldSerializeInMicrosDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MICROS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofNanos(1500))); + } + + @Test + public void shouldSerializeInMillis_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MILLIS"); + assertEquals("1000", mapper.writeValueAsString(Duration.ofSeconds(1))); + } + + @Test + public void shouldSerializeInMillisDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MILLIS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofNanos(1500000))); + } + + @Test + public void shouldSerializeInSeconds_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("SECONDS"); + assertEquals("60", mapper.writeValueAsString(Duration.ofMinutes(1))); + } + + @Test + public void shouldSerializeInSecondsDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("SECONDS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofMillis(1500))); + } + + @Test + public void shouldSerializeInMinutes_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MINUTES"); + assertEquals("60", mapper.writeValueAsString(Duration.ofHours(1))); + } + + @Test + public void shouldSerializeInMinutesDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("MINUTES"); + assertEquals("1", mapper.writeValueAsString(Duration.ofSeconds(90))); + } + + @Test + public void shouldSerializeInHours_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("HOURS"); + assertEquals("24", mapper.writeValueAsString(Duration.ofDays(1))); + } + + @Test + public void shouldSerializeInHoursDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("HOURS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofMinutes(90))); + } + + @Test + public void shouldSerializeInHalfDays_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("HALF_DAYS"); + assertEquals("2", mapper.writeValueAsString(Duration.ofDays(1))); + } + + @Test + public void shouldSerializeInHalfDaysDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("DAYS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofHours(30))); + } + + @Test + public void shouldSerializeInDays_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("DAYS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofDays(1))); + } + + @Test + public void shouldSerializeInDaysDiscardingFractions_whenSetAsPattern() throws Exception + { + ObjectMapper mapper = _mapperForPatternOverride("DAYS"); + assertEquals("1", mapper.writeValueAsString(Duration.ofHours(36))); + } + + protected ObjectMapper _mapperForPatternOverride(String pattern) { + ObjectMapper mapper = mapperBuilder() + .withConfigOverride(Duration.class, + cfg -> cfg.setFormat(JsonFormat.Value.forPattern(pattern))) + .enable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .build(); + return mapper; + } + + // [datetime#224] + @Test + public void testDurationFormatOverrideMinutes() throws Exception + { + assertEquals(a2q("{'mins':120}"), + WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .writeValueAsString(new MyDto224(Duration.ofHours(2)))); + } + + // [datetime#282] + @Test + public void testDurationFormatOverrideSeconds() throws Exception + { + final Duration maxDuration = Duration.ofSeconds(Long.MIN_VALUE); + assertEquals(a2q("{'duration':"+Long.MIN_VALUE+"}"), + WRITER + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .writeValueAsString(new Bean282(maxDuration))); + } + +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/InstantSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/InstantSerTest.java new file mode 100644 index 0000000000..e56f2f8540 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/InstantSerTest.java @@ -0,0 +1,189 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.datetime.util.DecimalUtils; + +import static org.junit.jupiter.api.Assertions.*; + +public class InstantSerTest extends ModuleTestBase +{ + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; + + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerializationAsTimestamp01Nanoseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + + assertNotNull(value); + assertEquals(NO_NANOSECS_SER, value); + } + + @Test + public void testSerializationAsTimestamp01Milliseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("0", value); + } + + @Test + public void testSerializationAsTimestamp02Nanoseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789.183917322", value); + } + + @Test + public void testSerializationAsTimestamp02Milliseconds() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789183", value); + } + + @Test + public void testSerializationAsTimestamp03Nanoseconds() throws Exception + { + Instant date = Instant.now(); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(DecimalUtils.toDecimal(date.getEpochSecond(), date.getNano()), value); + } + + @Test + public void testSerializationAsTimestamp03Milliseconds() throws Exception + { + Instant date = Instant.now(); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(Long.toString(date.toEpochMilli()), value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + Instant date = Instant.ofEpochSecond(0L); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsString03() throws Exception + { + Instant date = Instant.now(); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = m.writeValueAsString(date); + assertEquals("[\"" + Instant.class.getName() + "\",123456789.183917322]", value); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + Instant date = Instant.ofEpochSecond(123456789L, 183917322); + ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = m.writeValueAsString(date); + assertEquals("[\"" + Instant.class.getName() + "\",123456789183]", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + Instant date = Instant.now(); + ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = m.writeValueAsString(date); + assertEquals("[\"" + Instant.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value); + } + + static class Pojo1 { + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) + public Instant t1 = Instant.parse("2022-04-27T12:00:00Z"); + public Instant t2 = t1; + } + + @Test + public void testShapeInt() throws Exception { + String json1 = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new Pojo1()); + assertEquals("{\"t1\":1651060800000,\"t2\":1651060800.000000000}", json1); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/LocalDateSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/LocalDateSerTest.java new file mode 100644 index 0000000000..ec7d5e4b45 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/LocalDateSerTest.java @@ -0,0 +1,191 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDate; +import java.time.Month; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateSerTest + extends ModuleTestBase +{ + final static class EpochDayWrapper { + @JsonFormat(shape=JsonFormat.Shape.NUMBER_INT) + public LocalDate value; + + public EpochDayWrapper() { } + public EpochDayWrapper(LocalDate v) { value = v; } + } + + static class VanillaWrapper { + public LocalDate value; + + public VanillaWrapper() { } + public VanillaWrapper(LocalDate v) { value = v; } + } + + // [modules-java8#46] + static class Holder46 { + public LocalDate localDate; + + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT) + public Object object; + + public Holder46(LocalDate localDate, Object object) { + this.localDate = localDate; + this.object = object; + } + } + + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerializationAsTimestamp01() throws Exception + { + LocalDate date = LocalDate.of(1986, Month.JANUARY, 17); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + + assertNotNull(value); + assertEquals("[1986,1,17]", value); + } + + @Test + public void testSerializationAsTimestamp02() throws Exception + { + LocalDate date = LocalDate.of(2013, Month.AUGUST, 21); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + + assertNotNull(value); + assertEquals("[2013,8,21]", value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + LocalDate date = LocalDate.of(1986, Month.JANUARY, 17); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + + assertNotNull(value); + assertEquals('"' + date.toString() + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + LocalDate date = LocalDate.of(2013, Month.AUGUST, 21); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertNotNull(value); + assertEquals('"' + date.toString() + '"', value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + LocalDate date = LocalDate.of(2005, Month.NOVEMBER, 5); + String value = mapper.writeValueAsString(date); + + assertNotNull(value); + assertEquals("[\"" + LocalDate.class.getName() + "\",\"" + date.toString() + "\"]", value); + } + + // [modules-java8#46] + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + final LocalDate localDate = LocalDate.of(2017, 12, 5); + String json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new Holder46(localDate, localDate)); + assertEquals(a2q("{\"localDate\":[2017,12,5],\"object\":{\"java.time.LocalDate\":[2017,12,5]}}"), + json); + } + + @Test + public void testConfigOverrides() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forPattern("yyyy_MM_dd"))) + .build(); + LocalDate date = LocalDate.of(2005, Month.NOVEMBER, 5); + VanillaWrapper input = new VanillaWrapper(date); + final String EXP_DATE = "\"2005_11_05\""; + String json = mapper.writeValueAsString(input); + assertEquals("{\"value\":"+EXP_DATE+"}", json); + assertEquals(EXP_DATE, mapper.writeValueAsString(date)); + + // and read back, too + VanillaWrapper output = mapper.readValue(json, VanillaWrapper.class); + assertEquals(input.value, output.value); + LocalDate date2 = mapper.readValue(EXP_DATE, LocalDate.class); + assertEquals(date, date2); + } + + @Test + public void testConfigOverridesToEpochDay() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .withConfigOverride(LocalDate.class, + o -> o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER_INT))) + .build(); + LocalDate date = LocalDate.ofEpochDay(1000); + VanillaWrapper input = new VanillaWrapper(date); + final String EXP_DATE = "1000"; + String json = mapper.writeValueAsString(input); + assertEquals("{\"value\":"+EXP_DATE+"}", json); + assertEquals(EXP_DATE, mapper.writeValueAsString(date)); + + // and read back, too + VanillaWrapper output = mapper.readValue(json, VanillaWrapper.class); + assertEquals(input.value, output.value); + LocalDate date2 = mapper.readValue(EXP_DATE, LocalDate.class); + assertEquals(date, date2); + } + + @Test + public void testCustomFormatToEpochDay() throws Exception + { + EpochDayWrapper w = MAPPER.readValue("{\"value\": 1000}", EpochDayWrapper.class); + LocalDate date = w.value; + assertNotNull(date); + assertEquals(LocalDate.ofEpochDay(1000), date); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerTest.java new file mode 100644 index 0000000000..17a7c4b564 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/LocalDateTimeSerTest.java @@ -0,0 +1,194 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDateTime; +import java.time.Month; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalDateTimeSerTest + extends ModuleTestBase +{ + static class LDTWrapper { + @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss") + public LocalDateTime value; + + public LDTWrapper(LocalDateTime v) { value = v; } + } + + // 05-Feb-2025, tatu: Use Jackson 2.x defaults wrt as-timestamps + // serialization + private final static ObjectMapper MAPPER = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + @Test + public void testSerializationAsTimestamp01() throws Exception + { + LocalDateTime time = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43); + assertEquals("[1986,1,17,15,43]", + MAPPER.writeValueAsString(time)); + } + + @Test + public void testSerializationAsTimestamp02() throws Exception + { + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 57); + String value = MAPPER.writeValueAsString(time); + + assertEquals("[2013,8,21,9,22,57]", value); + } + + @Test + public void testSerializationAsTimestamp03Nanosecond() throws Exception + { + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); + + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[2013,8,21,9,22,0,57]", value); + } + + @Test + public void testSerializationAsTimestamp03Millisecond() throws Exception + { + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[2013,8,21,9,22,0,0]", value); + } + + @Test + public void testSerializationAsTimestamp04Nanosecond() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[2005,11,5,22,31,5,829837]", value); + } + + @Test + public void testSerializationAsTimestamp04Millisecond() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 422829837); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[2005,11,5,22,31,5,422]", value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + LocalDateTime time = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43, 05); + final ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + assertEquals("\"1986-01-17T15:43:05\"", m.writeValueAsString(time)); + } + + @Test + public void testSerializationAsString02() throws Exception + { + LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 57); + + final ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + String value = m.writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value); + } + + @Test + public void testSerializationAsString03() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + final ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + String value = m.writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value); + } + + @Test + public void testSerializationWithFormatOverride() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 999000); + assertEquals(a2q("{'value':'2005-11-05A22:31:05'}"), + MAPPER.writeValueAsString(new LDTWrapper(time))); + + ObjectMapper m = mapperBuilder().withConfigOverride(LocalDateTime.class, + cfg -> cfg.setFormat(JsonFormat.Value.forPattern("yyyy-MM-dd'X'HH:mm"))) + .build(); + assertEquals(a2q("'2005-11-05X22:31'"), m.writeValueAsString(time)); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + + final ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = m.writeValueAsString(time); + assertEquals("[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,829837]]", value); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + final ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 422829837); + String value = m.writeValueAsString(time); + assertEquals("[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,422]]", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + final ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); + String value = m.writeValueAsString(time); + assertEquals("[\"" + LocalDateTime.class.getName() + "\",\"" + time.toString() + "\"]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/LocalTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/LocalTimeSerTest.java new file mode 100644 index 0000000000..cf8c8dd13d --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/LocalTimeSerTest.java @@ -0,0 +1,199 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectWriter; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.annotation.JsonSerialize; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class LocalTimeSerTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + private final ObjectWriter writer = MAPPER.writer(); + + // [modules-java8#115] + static class CustomLocalTimeSerializer extends LocalTimeSerializer { + public CustomLocalTimeSerializer() { + // Default doesn't cut it for us. + super(DateTimeFormatter.ofPattern("HH/mm")); + } + } + + static class CustomWrapper { + @JsonSerialize(using = CustomLocalTimeSerializer.class) + public LocalTime value; + + public CustomWrapper(LocalTime v) { value = v; } + } + + @Test + public void testSerializationAsTimestamp01() throws Exception + { + String json = writer.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(LocalTime.of(15, 43)); + assertEquals("[15,43]", json, "The value is not correct."); + } + + @Test + public void testSerializationAsTimestamp02() throws Exception + { + String json = writer.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(LocalTime.of(9, 22, 57)); + assertEquals("[9,22,57]", json, "The value is not correct."); + } + + @Test + public void testSerializationAsTimestamp03Nanoseconds() throws Exception + { + String json = writer.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, + SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(LocalTime.of(9, 22, 0, 57)); + assertEquals("[9,22,0,57]", json, "The value is not correct."); + } + + @Test + public void testSerializationAsTimestamp03Milliseconds() throws Exception + { + LocalTime time = LocalTime.of(9, 22, 0, 57); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .build(); + String value = mapper.writeValueAsString(time); + + assertEquals("[9,22,0,0]", value, "The value is not correct."); + } + + @Test + public void testSerializationAsTimestamp04Nanoseconds() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true) + .build(); + String value = mapper.writeValueAsString(time); + assertEquals("[22,31,5,829837]", value, "The value is not correct."); + } + + @Test + public void testSerializationAsTimestamp04Milliseconds() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 422829837); + ObjectMapper mapper = newMapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .build(); + String value = mapper.writeValueAsString(time); + assertEquals("[22,31,5,422]", value, "The value is not correct."); + } + + @Test + public void testSerializationAsString01() throws Exception + { + LocalTime time = LocalTime.of(15, 43, 20); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + assertEquals("\"15:43:20\"", mapper.writeValueAsString(time)); + } + + @Test + public void testSerializationAsString02() throws Exception + { + LocalTime time = LocalTime.of(9, 22, 57); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + String value = mapper.writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value, "The value is not correct."); + } + + @Test + public void testSerializationAsString03() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper m = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + String value = m.writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value, "The value is not correct."); + } + + // [modules-java8#115] + @Test + public void testWithCustomSerializer() throws Exception + { + String json = MAPPER.writeValueAsString(new CustomWrapper(LocalTime.of(15, 43))); + assertEquals("{\"value\":\"15/43\"}", json, "The value is not correct."); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper m = newMapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .enable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String json = m.writeValueAsString(time); + + assertEquals("[\"" + LocalTime.class.getName() + "\",[22,31,5,829837]]", json, + "The value is not correct."); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 422829837); + + ObjectMapper m = newMapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String json = m.writeValueAsString(time); + assertEquals("[\"" + LocalTime.class.getName() + "\",[22,31,5,422]]", json, + "The value is not correct."); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + LocalTime time = LocalTime.of(22, 31, 5, 829837); + ObjectMapper m = newMapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = m.writeValueAsString(time); + + assertEquals("[\"" + LocalTime.class.getName() + "\",\"" + time.toString() + "\"]", value, + "The value is not correct."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/MonthDaySerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/MonthDaySerTest.java new file mode 100644 index 0000000000..fc6b37c548 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/MonthDaySerTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Month; +import java.time.MonthDay; +import java.time.temporal.TemporalAccessor; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class MonthDaySerTest + extends ModuleTestBase +{ + private ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerialization01() throws Exception + { + assertEquals("\"--01-17\"", + MAPPER.writeValueAsString(MonthDay.of(Month.JANUARY, 17))); + } + + @Test + public void testSerialization02() throws Exception + { + assertEquals("\"--08-21\"", + MAPPER.writeValueAsString(MonthDay.of(Month.AUGUST, 21))); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + final ObjectMapper mapper = mapperBuilder() + .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .build(); + MonthDay monthDay = MonthDay.of(Month.NOVEMBER, 5); + String value = mapper.writeValueAsString(monthDay); + assertEquals("[\"" + MonthDay.class.getName() + "\",\"--11-05\"]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerTest.java new file mode 100644 index 0000000000..27c3d02c1d --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/OffsetDateTimeSerTest.java @@ -0,0 +1,290 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.util.TimeZone; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.datetime.util.DecimalUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OffsetDateTimeSerTest + extends ModuleTestBase +{ + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + + private static final ZoneId Z1 = ZoneId.of("America/Chicago"); + + private static final ZoneId Z2 = ZoneId.of("America/Anchorage"); + + private static final ZoneId Z3 = ZoneId.of("America/Los_Angeles"); + + static class Wrapper { + @JsonFormat( + pattern="yyyy_MM_dd'T'HH:mm:ssZ", + shape=JsonFormat.Shape.STRING) + public OffsetDateTime value; + + public Wrapper() { } + public Wrapper(OffsetDateTime v) { value = v; } + } + + private ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerializationAsTimestamp01Nanoseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("0.0", value); + } + + @Test + public void testSerializationAsTimestamp01Milliseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("0", value); + } + + @Test + public void testSerializationAsTimestamp02Nanoseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789.183917322", value); + } + + @Test + public void testSerializationAsTimestamp02Milliseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789183", value); + } + + @Test + public void testSerializationAsTimestamp03Nanoseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano()), value); + } + + @Test + public void testSerializationAsTimestamp03Milliseconds() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(Long.toString(date.toInstant().toEpochMilli()), value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z1).format(date) + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z2).format(date) + '"', value); + } + + @Test + public void testSerializationAsString03() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z3).format(date) + '"', value); + } + + // [modules-java#254] + @Test + public void testSerializationWithJsonFormat() throws Exception + { + OffsetDateTime t1 = OffsetDateTime.parse("2022-04-27T12:00:00+02:00"); + Wrapper input = new Wrapper(t1); + + // pattern="yyyy_MM_dd'T'HH:mm:ssZ" + assertEquals(a2q("{'value':'2022_04_27T12:00:00+0200'}"), + MAPPER.writeValueAsString(input)); + + ObjectMapper m = mapperBuilder().withConfigOverride(OffsetDateTime.class, + cfg -> cfg.setFormat(JsonFormat.Value.forPattern("yyyy.MM.dd'x'HH:mm:ss"))) + .build(); + assertEquals(a2q("'2022.04.27x12:00:00'"), m.writeValueAsString(t1)); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone01() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z1)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone02() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone03() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z3)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, + SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build() + .writeValueAsString(date); + assertEquals("[\"" + OffsetDateTime.class.getName() + "\",123456789.183917322]", value); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + OffsetDateTime date = OffsetDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .build() + .writeValueAsString(date); + assertEquals("[\"" + OffsetDateTime.class.getName() + "\",123456789183]", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + ObjectMapper m = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .build(); + String value = m.writeValueAsString(date); + assertEquals("[\"" + OffsetDateTime.class.getName() + "\",\"" + + FORMATTER.withZone(Z3).format(date) + "\"]", value); + } + + @Test + public void testSerializationWithTypeInfoAndMapperTimeZone() throws Exception + { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build() + .writer() + .with(TimeZone.getTimeZone(Z3)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + + assertEquals("[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOn() throws Exception { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z2 + assertEquals("\"" + FORMATTER.format(date.atZoneSameInstant(Z2)) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOff() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z3 + assertEquals("\"" + FORMATTER.format(date) + "\"", value); + } + + static class Pojo1 { + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) + public OffsetDateTime t1 = OffsetDateTime.parse("2022-04-27T12:00:00+02:00"); + public OffsetDateTime t2 = t1; + } + + @Test + public void testShapeInt() throws Exception { + String json1 = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(new Pojo1()); + assertEquals("{\"t1\":1651053600000,\"t2\":1651053600.000000000}", json1); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/OffsetTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/OffsetTimeSerTest.java new file mode 100644 index 0000000000..cbb3dd1a4a --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/OffsetTimeSerTest.java @@ -0,0 +1,171 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.*; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetTimeSerTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerializationAsTimestamp01() throws Exception + { + OffsetTime time = OffsetTime.of(15, 43, 0, 0, ZoneOffset.of("+0300")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(time); + assertEquals("[15,43,\"+03:00\"]", value); + } + + @Test + public void testSerializationAsTimestamp02() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 57, 0, ZoneOffset.of("-0630")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(time); + assertEquals("[9,22,57,\"-06:30\"]", value); + } + + @Test + public void testSerializationAsTimestamp03Nanoseconds() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 0, 57, ZoneOffset.of("-0630")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[9,22,0,57,\"-06:30\"]", value); + } + + @Test + public void testSerializationAsTimestamp03Milliseconds() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 0, 57, ZoneOffset.of("-0630")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[9,22,0,0,\"-06:30\"]", value); + } + + @Test + public void testSerializationAsTimestamp04Nanoseconds() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[22,31,5,829837,\"+11:00\"]", value); + } + + @Test + public void testSerializationAsTimestamp04Milliseconds() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 422829837, ZoneOffset.of("+1100")); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); + assertEquals("[22,31,5,422,\"+11:00\"]", value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + OffsetTime time = OffsetTime.of(15, 43, 0, 0, ZoneOffset.of("+0300")); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + OffsetTime time = OffsetTime.of(9, 22, 57, 0, ZoneOffset.of("-0630")); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value); + } + + @Test + public void testSerializationAsString03() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(time); + assertEquals('"' + time.toString() + '"', value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + assertEquals("[\"" + OffsetTime.class.getName() + "\",[22,31,5,829837,\"+11:00\"]]", + mapper.writeValueAsString(time)); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 422829837, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) + .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + + assertEquals("[\"" + OffsetTime.class.getName() + "\",[22,31,5,422,\"+11:00\"]]", + mapper.writeValueAsString(time)); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + OffsetTime time = OffsetTime.of(22, 31, 5, 829837, ZoneOffset.of("+1100")); + + final ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + + assertEquals("[\"" + OffsetTime.class.getName() + "\",\"" + time.toString() + "\"]", + mapper.writeValueAsString(time)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java new file mode 100644 index 0000000000..a4e4ae5be7 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java @@ -0,0 +1,66 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Month; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectWriter; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; + +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OneBasedMonthSerTest extends ModuleTestBase +{ + static class Wrapper { + public Month month; + + public Wrapper(Month m) { month = m; } + public Wrapper() { } + } + + @Test + public void testSerializationFromEnum() throws Exception + { + assertEquals( "\"JANUARY\"" , writerForOneBased() + .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + .writeValueAsString(Month.JANUARY)); + assertEquals( "\"JANUARY\"" , writerForZeroBased() + .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + .writeValueAsString(Month.JANUARY)); + } + + @Test + public void testSerializationFromEnumWithPattern_oneBased() throws Exception + { + ObjectWriter w = writerForOneBased().with(SerializationFeature.WRITE_ENUMS_USING_INDEX); + assertEquals( "{\"month\":1}" , w.writeValueAsString(new Wrapper(Month.JANUARY))); + } + + @Test + public void testSerializationFromEnumWithPattern_zeroBased() throws Exception + { + ObjectWriter w = writerForZeroBased().with(SerializationFeature.WRITE_ENUMS_USING_INDEX); + assertEquals( "{\"month\":0}" , w.writeValueAsString(new Wrapper(Month.JANUARY))); + } + + + private ObjectWriter writerForZeroBased() { + return JsonMapper.builder() + .addModule(new JavaTimeModule().disable(JavaTimeFeature.ONE_BASED_MONTHS)) + .build() + .writer(); + } + + private ObjectWriter writerForOneBased() { + return JsonMapper.builder() + .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .build() + .writer(); + } + +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/PeriodSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/PeriodSerTest.java new file mode 100644 index 0000000000..a6335aec80 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/PeriodSerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Period; +import java.time.temporal.TemporalAmount; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PeriodSerTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerialization01() throws Exception + { + assertEquals(q("P1Y6M15D"), MAPPER.writeValueAsString(Period.of(1, 6, 15))); + } + + @Test + public void testSerialization02() throws Exception + { + assertEquals(q("P21D"), MAPPER.writeValueAsString(Period.of(0, 0, 21))); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + Period period = Period.of(5, 1, 12); + final ObjectMapper mapper = mapperBuilder() + .addMixIn(TemporalAmount.class, MockObjectConfiguration.class) + .build(); + String value = mapper.writeValueAsString(period); + assertEquals("[" + q(Period.class.getName()) + ",\"P5Y1M12D\"]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..901da8f3b7 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java @@ -0,0 +1,63 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import tools.jackson.core.json.JsonWriteFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.deser.LocalDateDeserializer; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestLocalDateSerializationWithCustomFormatter +{ + @ParameterizedTest + @MethodSource("customFormatters") + void testSerialization(DateTimeFormatter formatter) throws Exception { + LocalDate date = LocalDate.now(); + assertTrue(serializeWith(date, formatter).contains(date.format(formatter)), + "Serialized value should contain the formatted date"); + } + + private String serializeWith(LocalDate date, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) + .addModule(new SimpleModule() + .addSerializer(new LocalDateSerializer(f))) + .build(); + return mapper.writeValueAsString(date); + } + + @ParameterizedTest + @MethodSource("customFormatters") + void testDeserialization(DateTimeFormatter formatter) throws Exception { + LocalDate date = LocalDate.now(); + assertEquals(date, deserializeWith(date.format(formatter), formatter), + "Deserialized value should match the original date"); + } + + private LocalDate deserializeWith(String json, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addDeserializer(LocalDate.class, new LocalDateDeserializer(f))) + .build(); + return mapper.readValue("\"" + json + "\"", LocalDate.class); + } + + static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.BASIC_ISO_DATE, + DateTimeFormatter.ISO_DATE, + DateTimeFormatter.ISO_LOCAL_DATE, + DateTimeFormatter.ISO_ORDINAL_DATE, + DateTimeFormatter.ISO_WEEK_DATE, + DateTimeFormatter.ofPattern("MM/dd/yyyy") + ); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..4f5b32ac94 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java @@ -0,0 +1,56 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.deser.LocalDateTimeDeserializer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestLocalDateTimeSerializationWithCustomFormatter +{ + @ParameterizedTest + @MethodSource("customFormatters") + void testSerialization(DateTimeFormatter formatter) throws Exception { + LocalDateTime dateTime = LocalDateTime.now(); + assertTrue(serializeWith(dateTime, formatter).contains(dateTime.format(formatter))); + } + + private String serializeWith(LocalDateTime dateTime, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addSerializer(new LocalDateTimeSerializer(f))) + .build(); + return mapper.writeValueAsString(dateTime); + } + + @ParameterizedTest + @MethodSource("customFormatters") + void testDeserialization(DateTimeFormatter formatter) throws Exception { + LocalDateTime dateTime = LocalDateTime.now(); + assertEquals(dateTime, deserializeWith(dateTime.format(formatter), formatter)); + } + + private LocalDateTime deserializeWith(String json, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(f))) + .build(); + return mapper.readValue("\"" + json + "\"", LocalDateTime.class); + } + + static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.ISO_DATE_TIME, + DateTimeFormatter.ISO_LOCAL_DATE_TIME + ); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..76570a459c --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java @@ -0,0 +1,55 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.stream.Stream; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.deser.LocalTimeDeserializer; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestLocalTimeSerializationWithCustomFormatter +{ + @ParameterizedTest + @MethodSource("customFormatters") + void testSerialization(DateTimeFormatter formatter) throws Exception { + LocalTime dateTime = LocalTime.now(); + assertTrue(serializeWith(dateTime, formatter).contains(dateTime.format(formatter))); + } + + private String serializeWith(LocalTime dateTime, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addSerializer(new LocalTimeSerializer(f))) + .build(); + return mapper.writeValueAsString(dateTime); + } + + @ParameterizedTest + @MethodSource("customFormatters") + void testDeserialization(DateTimeFormatter formatter) throws Exception { + LocalTime dateTime = LocalTime.now(); + assertEquals(dateTime, deserializeWith(dateTime.format(formatter), formatter)); + } + + private LocalTime deserializeWith(String json, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addDeserializer(LocalTime.class, new LocalTimeDeserializer(f))) + .build(); + return mapper.readValue("\"" + json + "\"", LocalTime.class); + } + + static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.ISO_LOCAL_TIME, + DateTimeFormatter.ISO_TIME + ); + } +} \ No newline at end of file diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..77a294227f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java @@ -0,0 +1,56 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.deser.YearMonthDeserializer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestYearMonthSerializationWithCustomFormatter { + + @ParameterizedTest + @MethodSource("customFormatters") + void testSerialization(DateTimeFormatter formatter) throws Exception { + YearMonth dateTime = YearMonth.now(); + assertTrue(serializeWith(dateTime, formatter).contains(dateTime.format(formatter))); + } + + private String serializeWith(YearMonth dateTime, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addSerializer(new YearMonthSerializer(f))) + .build(); + return mapper.writeValueAsString(dateTime); + } + + @ParameterizedTest + @MethodSource("customFormatters") + void testDeserialization(DateTimeFormatter formatter) throws Exception { + YearMonth dateTime = YearMonth.now(); + assertEquals(dateTime, deserializeWith(dateTime.format(formatter), formatter)); + } + + private YearMonth deserializeWith(String json, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addDeserializer(YearMonth.class, new YearMonthDeserializer(f))) + .build(); + return mapper.readValue("\"" + json + "\"", YearMonth.class); + } + + static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.ofPattern("uuuu-MM"), + DateTimeFormatter.ofPattern("uu-M") + ); + } +} \ No newline at end of file diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestYearSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestYearSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..69ab031d17 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestYearSerializationWithCustomFormatter.java @@ -0,0 +1,56 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Year; +import java.time.format.DateTimeFormatter; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.deser.YearDeserializer; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestYearSerializationWithCustomFormatter +{ + @ParameterizedTest + @MethodSource("customFormatters") + void testSerialization(DateTimeFormatter formatter) throws Exception { + Year year = Year.now(); + String expected = "\"" + year.format(formatter) + "\""; + assertEquals(expected, serializeWith(year, formatter)); + } + + private String serializeWith(Year dateTime, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addSerializer(new YearSerializer(f))) + .build(); + return mapper.writeValueAsString(dateTime); + } + + @ParameterizedTest + @MethodSource("customFormatters") + void testDeserialization(DateTimeFormatter formatter) throws Exception { + Year year = Year.now(); + assertEquals(year, deserializeWith(year.format(formatter), formatter)); + } + + private Year deserializeWith(String json, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule() + .addDeserializer(Year.class, new YearDeserializer(f))) + .build(); + return mapper.readValue("\"" + json + "\"", Year.class); + } + + static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.ofPattern("yyyy"), + DateTimeFormatter.ofPattern("yy") + ); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java new file mode 100644 index 0000000000..6818ddf525 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java @@ -0,0 +1,46 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestZonedDateTimeSerializationWithCustomFormatter { + + @MethodSource("customFormatters") + @ParameterizedTest + public void testSerialization(DateTimeFormatter formatter) throws Exception { + ZonedDateTime zonedDateTime = ZonedDateTime.now(); + assertTrue(serializeWith(zonedDateTime, formatter).contains(zonedDateTime.format(formatter.withZone(ZoneOffset.UTC)))); + } + + private String serializeWith(ZonedDateTime zonedDateTime, DateTimeFormatter f) throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new SimpleModule().addSerializer( + new ZonedDateTimeSerializer(f))) + .defaultTimeZone(TimeZone.getTimeZone("UTC")) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + return mapper.writeValueAsString(zonedDateTime); + } + + public static Stream customFormatters() { + return Stream.of( + DateTimeFormatter.ISO_ZONED_DATE_TIME, + DateTimeFormatter.ISO_OFFSET_DATE_TIME, + DateTimeFormatter.ISO_LOCAL_DATE_TIME, + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") + ); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/WriteNanosecondsTest.java b/src/test/java/tools/jackson/databind/datetime/ser/WriteNanosecondsTest.java new file mode 100644 index 0000000000..f4866c83fd --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/WriteNanosecondsTest.java @@ -0,0 +1,119 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.*; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; + +public class WriteNanosecondsTest extends ModuleTestBase +{ + public static final ZoneId UTC = ZoneId.of("UTC"); + + // 05-Feb-2025, tatu: Use Jackson 2.x defaults wrt as-timestamps + // serialization + private final static ObjectMapper MAPPER = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + public static class DummyClass { + @JsonFormat(with = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + private final T nanoseconds; + + @JsonFormat(without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + private final T notNanoseconds; + + DummyClass(T t) { + this.nanoseconds = t; + this.notNanoseconds = t; + } + } + + @Test + public void testSerializeDurationWithAndWithoutNanoseconds() throws Exception { + DummyClass value = new DummyClass<>(Duration.ZERO); + + String json = MAPPER.writer() + .with(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .writeValueAsString(value); + + assertThat(json).contains("\"nanoseconds\":0.0"); + assertThat(json).contains("\"notNanoseconds\":0"); + } + + @Test + public void testSerializeInstantWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>(Instant.EPOCH); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":0.0")); + assertTrue(json.contains("\"notNanoseconds\":0")); + } + + @Test + public void testSerializeLocalDateTimeWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>( + // Nanos will only be written if it's non-zero + LocalDateTime.of(1970, 1, 1, 0, 0, 0, 1) + ); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":[1970,1,1,0,0,0,1]")); + assertTrue(json.contains("\"notNanoseconds\":[1970,1,1,0,0,0,0]")); + } + + @Test + public void testSerializeLocalTimeWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>( + // Nanos will only be written if it's non-zero + LocalTime.of(0, 0, 0, 1) + ); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":[0,0,0,1]")); + assertTrue(json.contains("\"notNanoseconds\":[0,0,0,0]")); + } + + @Test + public void testSerializeOffsetDateTimeWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>(OffsetDateTime.ofInstant(Instant.EPOCH, UTC)); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":0.0")); + assertTrue(json.contains("\"notNanoseconds\":0")); + } + + @Test + public void testSerializeOffsetTimeWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>( + // Nanos will only be written if it's non-zero + OffsetTime.of(0,0,0, 1 , ZoneOffset.UTC) + ); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":[0,0,0,1,\"Z\"]")); + assertTrue(json.contains("\"notNanoseconds\":[0,0,0,0,\"Z\"]")); + } + + @Test + public void testSerializeZonedDateTimeWithAndWithoutNanoseconds() throws Exception { + DummyClass input = new DummyClass<>(ZonedDateTime.ofInstant(Instant.EPOCH, UTC)); + + String json = MAPPER.writeValueAsString(input); + + assertTrue(json.contains("\"nanoseconds\":0.0")); + assertTrue(json.contains("\"notNanoseconds\":0")); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java b/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java new file mode 100644 index 0000000000..ccb5605515 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java @@ -0,0 +1,94 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class WriteZoneIdTest extends ModuleTestBase +{ + static class DummyClassWithDate { + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "dd-MM-yyyy'T'hh:mm:ss Z", + with = JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID) + public ZonedDateTime date; + + DummyClassWithDate() { } + + public DummyClassWithDate(ZonedDateTime date) { + this.date = date; + } + } + + private static ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerialization01() throws Exception + { + ZoneId id = ZoneId.of("America/Chicago"); + String value = MAPPER.writeValueAsString(id); + assertEquals("\"America/Chicago\"", value); + } + + @Test + public void testSerialization02() throws Exception + { + ZoneId id = ZoneId.of("America/Anchorage"); + String value = MAPPER.writeValueAsString(id); + assertEquals("\"America/Anchorage\"", value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + ZoneId id = ZoneId.of("America/Denver"); + ObjectMapper mapper = mapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .addModule(new JavaTimeModule()) + .build(); + String value = mapper.writeValueAsString(id); + assertEquals("[\"java.time.ZoneId\",\"America/Denver\"]", value); + } + + @Test + public void testJacksonAnnotatedPOJOWithDateWithTimezoneToJson() throws Exception + { + String ZONE_ID_STR = "Asia/Krasnoyarsk"; + final ZoneId ZONE_ID = ZoneId.of(ZONE_ID_STR); + + DummyClassWithDate input = new DummyClassWithDate(ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), ZONE_ID)); + + // 30-Jun-2016, tatu: Exact time seems to vary a bit based on DST, so let's actually + // just verify appending of timezone id itself: + String json = MAPPER.writeValueAsString(input); + if (!json.contains("\"01-01-1970T")) { + fail("Should contain time prefix, did not: "+json); + } + String match = String.format("[%s]", ZONE_ID_STR); + if (!json.contains(match)) { + fail("Should contain zone id "+match+", does not: "+json); + } + } + + @Test + public void testMapSerialization() throws Exception { + final ZonedDateTime datetime = ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Warsaw]"); + final HashMap map = new HashMap<>(); + map.put(datetime, ""); + String json = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .writeValueAsString(map); + assertEquals("{\"2007-12-03T10:15:30+01:00[Europe/Warsaw]\":\"\"}", json); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/YearMonthSerializationTest.java b/src/test/java/tools/jackson/databind/datetime/ser/YearMonthSerializationTest.java new file mode 100644 index 0000000000..06949967a2 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/YearMonthSerializationTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Month; +import java.time.YearMonth; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearMonthSerializationTest + extends ModuleTestBase +{ + private static class SimpleAggregate + { + @JsonProperty("yearMonth") + @JsonFormat(pattern = "yyMM") + final YearMonth yearMonth; + + @JsonCreator + SimpleAggregate(@JsonProperty("yearMonth") YearMonth yearMonth) + { + this.yearMonth = yearMonth; + } + } + + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerializationAsTimestamp01() throws Exception + { + YearMonth yearMonth = YearMonth.of(1986, Month.JANUARY); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(yearMonth); + + assertNotNull(value); + assertEquals("[1986,1]", value); + } + + @Test + public void testSerializationAsTmestamp02() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(yearMonth); + + assertNotNull(value); + assertEquals("[2013,8]", value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + YearMonth yearMonth = YearMonth.of(1986, Month.JANUARY); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(yearMonth); + + assertNotNull(value); + assertEquals('"' + yearMonth.toString() + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(yearMonth); + assertEquals('"' + yearMonth.toString() + '"', value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + YearMonth yearMonth = YearMonth.of(2005, Month.NOVEMBER); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = mapper.writeValueAsString(yearMonth); + assertEquals("[\"" + YearMonth.class.getName() + "\",\"" + yearMonth.toString() + "\"]", value); + } + + @Test + public void testDeserializationAsTimestamp01() throws Exception + { + YearMonth yearMonth = YearMonth.of(1986, Month.JANUARY); + YearMonth value = MAPPER.readValue("[1986,1]", YearMonth.class); + assertEquals(yearMonth, value); + } + + @Test + public void testDeserializationAsTimestamp02() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + YearMonth value = MAPPER.readValue("[2013,8]", YearMonth.class); + assertEquals(yearMonth, value); + } + + @Test + public void testDeserializationAsString01() throws Exception + { + YearMonth yearMonth = YearMonth.of(1986, Month.JANUARY); + YearMonth value = MAPPER.readValue('"' + yearMonth.toString() + '"', YearMonth.class); + + assertNotNull(value); + assertEquals(yearMonth, value); + } + + @Test + public void testDeserializationAsString02() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + YearMonth value = this.MAPPER.readValue('"' + yearMonth.toString() + '"', YearMonth.class); + assertEquals(yearMonth, value); + } + + @Test + public void testDeserializationWithTypeInfo01() throws Exception + { + YearMonth yearMonth = YearMonth.of(2005, Month.NOVEMBER); + + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue("[\"" + YearMonth.class.getName() + "\",\"" + yearMonth.toString() + "\"]", Temporal.class); + assertInstanceOf(YearMonth.class, value, "The value should be a YearMonth."); + assertEquals(yearMonth, value); + } + + @Test + public void testSerializationWithPattern01() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + SimpleAggregate simpleAggregate = new SimpleAggregate(yearMonth); + String value = MAPPER.writeValueAsString(simpleAggregate); + assertEquals("{\"yearMonth\":\"1308\"}", value); + } + + @Test + public void testDeserializationWithPattern01() throws Exception + { + YearMonth yearMonth = YearMonth.of(2013, Month.AUGUST); + SimpleAggregate simpleAggregate = new SimpleAggregate(yearMonth); + + SimpleAggregate value = MAPPER.readValue("{\"yearMonth\":\"1308\"}", SimpleAggregate.class); + assertEquals(simpleAggregate.yearMonth, value.yearMonth); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/YearSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/YearSerTest.java new file mode 100644 index 0000000000..a0089f138d --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/YearSerTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.Year; +import java.time.temporal.Temporal; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class YearSerTest extends ModuleTestBase +{ + final static class YearAsStringWrapper { + @JsonFormat(shape = JsonFormat.Shape.STRING) + public Year value; + + public YearAsStringWrapper(Year value) { + this.value = value; + } + } + + // Defaults fine: year only serialized as String with explicit + // overrides + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testDefaultSerialization() throws Exception + { + assertEquals("1986", + MAPPER.writeValueAsString(Year.of(1986))); + assertEquals("2013", + MAPPER.writeValueAsString(Year.of(2013))); + } + + @Test + public void testAsStringSerializationViaAnnotation() throws Exception + { + assertEquals(a2q("{'value':'1972'}"), + MAPPER.writeValueAsString(new YearAsStringWrapper(Year.of(1972)))); + } + + @Test + public void testAsStringSerializationViaFormatConfig() throws Exception + { + final ObjectMapper asStringMapper = mapperBuilder() + .withConfigOverride(Year.class, o -> o.setFormat( + JsonFormat.Value.forShape(JsonFormat.Shape.STRING))) + .build(); + + assertEquals(q("2025"), + asStringMapper.writeValueAsString(Year.of(2025))); + } + + @Test + public void testSerializationWithTypeInfo() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + String value = mapper.writeValueAsString(Year.of(2005)); + assertEquals("[\"" + Year.class.getName() + "\",2005]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZoneIdSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/ZoneIdSerTest.java new file mode 100644 index 0000000000..1df4dc3f82 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZoneIdSerTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.ZoneId; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ZoneIdSerTest extends ModuleTestBase +{ + private ObjectMapper MAPPER = newMapper(); + + private final ObjectMapper MOCK_OBJECT_MIXIN_MAPPER = mapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .build(); + + @Test + public void testSerialization01() throws Exception + { + final String value = MAPPER.writeValueAsString(ZoneId.of("America/Chicago")); + assertEquals("\"America/Chicago\"", value); + } + + @Test + public void testSerialization02() throws Exception + { + final String value = MAPPER.writeValueAsString(ZoneId.of("America/Anchorage")); + assertEquals("\"America/Anchorage\"", value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + String value = MOCK_OBJECT_MIXIN_MAPPER.writeValueAsString(ZoneId.of("America/Denver")); + assertEquals("[\"java.time.ZoneId\",\"America/Denver\"]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZoneOffsetSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/ZoneOffsetSerTest.java new file mode 100644 index 0000000000..66ffa45067 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZoneOffsetSerTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZoneOffsetSerTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newMapper(); + + @Test + public void testSerialization01() throws Exception + { + ZoneOffset offset = ZoneOffset.of("Z"); + String value = MAPPER.writeValueAsString(offset); + assertEquals("\"Z\"", value); + } + + @Test + public void testSerialization02() throws Exception + { + ZoneOffset offset = ZoneOffset.of("+0300"); + String value = MAPPER.writeValueAsString(offset); + assertEquals("\"+03:00\"", value); + } + + @Test + public void testSerialization03() throws Exception + { + ZoneOffset offset = ZoneOffset.of("-0630"); + String value = MAPPER.writeValueAsString(offset); + assertEquals("\"-06:30\"", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + ObjectMapper mapper = newMapperBuilder() + .addMixIn(ZoneId.class, MockObjectConfiguration.class) + .build(); + ZoneOffset offset = ZoneOffset.of("+0415"); + String value = mapper.writeValueAsString(offset); + assertEquals("[\"" + ZoneOffset.class.getName() + "\",\"+04:15\"]", value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java new file mode 100644 index 0000000000..6b1b73828f --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java @@ -0,0 +1,968 @@ +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package tools.jackson.databind.datetime.ser; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.Locale; +import java.util.TimeZone; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.MockObjectConfiguration; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.datetime.util.DecimalUtils; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZonedDateTimeSerTest + extends ModuleTestBase +{ + private static final DateTimeFormatter FORMATTER_WITHOUT_ZONEID = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + + private static final ZoneId Z1 = ZoneId.of("America/Chicago"); + + private static final ZoneId Z2 = ZoneId.of("America/Anchorage"); + + private static final ZoneId Z3 = ZoneId.of("America/Los_Angeles"); + + private static final ZoneId UTC = ZoneOffset.UTC; + + private static final ZoneId DEFAULT_TZ = UTC; + + private static final ZoneId FIX_OFFSET = ZoneId.of("-08:00"); + + final static class Wrapper { + @JsonFormat(pattern="yyyy_MM_dd HH:mm:ss(Z)", + shape=JsonFormat.Shape.STRING) + public ZonedDateTime value; + + public Wrapper() { } + public Wrapper(ZonedDateTime v) { value = v; } + } + + final static class WrapperNumeric { + @JsonFormat(pattern="yyyyMMddHHmmss", + shape=JsonFormat.Shape.STRING, + timezone = "UTC") + public ZonedDateTime value; + + public WrapperNumeric() { } + public WrapperNumeric(ZonedDateTime v) { value = v; } + } + + private final ObjectMapper MAPPER = newMapper(); + private final ObjectReader READER = MAPPER.readerFor(ZonedDateTime.class); + private final ObjectMapper MAPPER_WITH_DEFAULT_TZ = newMapper(TimeZone.getDefault()); + + + @Test + public void testSerializationAsTimestamp01Nanoseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("0.0", value); + } + + @Test + public void testSerializationAsTimestamp01NegativeSeconds() throws Exception + { + // test for Issue #69 + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(-14159020000L, 183917322), UTC); + String serialized = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + ZonedDateTime actual = MAPPER.readValue(serialized, ZonedDateTime.class); + assertEquals(date, actual); + } + + @Test + public void testSerializationAsTimestamp01NegativeSecondsWithDefaults() throws Exception + { + // test for Issue #69 using default mapper config + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz", Locale.ENGLISH); + ZonedDateTime original = ZonedDateTime.parse("Apr 13 1969 05:05:38.599 UTC", dtf); + String serialized = MAPPER.writeValueAsString(original); + ZonedDateTime deserialized = MAPPER.readValue(serialized, ZonedDateTime.class); + assertEquals(original.getDayOfMonth(), deserialized.getDayOfMonth(), "The day is not correct."); + assertEquals(original.getMonthValue(), deserialized.getMonthValue(), "The month is not correct."); + assertEquals(original.getYear(), deserialized.getYear(), "The year is not correct."); + assertEquals(original.getHour(), deserialized.getHour(), "The hour is not correct."); + assertEquals(original.getMinute(), deserialized.getMinute(), "The minute is not correct."); + assertEquals(original.getSecond(), deserialized.getSecond(), "The second is not correct."); + assertEquals(original.getNano(), deserialized.getNano(), "The nano is not correct."); + assertEquals(ZoneId.of("UTC").getRules(), deserialized.getZone().getRules(), "The time zone is not correct."); + } + + @Test + public void testSerializationAsTimestamp01Milliseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("0", value); + } + + @Test + public void testSerializationAsTimestamp02Nanoseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789.183917322", value); + } + + @Test + public void testSerializationAsTimestamp02Milliseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("123456789183", value); + } + + @Test + public void testSerializationAsTimestamp03Nanoseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano()), value); + } + + @Test + public void testSerializationAsTimestamp03Milliseconds() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals(Long.toString(date.toInstant().toEpochMilli()), value); + } + + @Test + public void testSerializationAsString01() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z1).format(date) + '"', value); + } + + @Test + public void testSerializationAsString02() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z2).format(date) + '"', value); + } + + @Test + public void testSerializationAsString03() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + + FORMATTER.withZone(Z3).format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone01() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z1)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone02() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithMapperTimeZone03() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z3)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .writeValueAsString(date); + assertEquals('"' + FORMATTER.format(date) + '"', value); + } + + @Test + public void testSerializationAsStringWithZoneIdOff() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, false) + .build(); + + assertEquals(q(FORMATTER.withZone(Z3).format(date)), + mapper.writeValueAsString(date)); + } + + @Test + public void testSerializationAsStringWithZoneIdOffAndMapperTimeZone() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = newMapper() + .writer() + .with(TimeZone.getTimeZone(Z3)) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .writeValueAsString(date); + assertEquals(q(FORMATTER.format(date)), value); + } + + @Test + public void testSerializationAsStringWithZoneIdOn() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapperBuilder() + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true) + .build(); + String value = mapper.writeValueAsString(date); + assertEquals("\"" + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(date) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOnAndACustomFormatter() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + // With a custom DateTimeFormatter without a ZoneId. + String value = newMapperBuilder().addModule( + new SimpleModule().addSerializer(new ZonedDateTimeSerializer(FORMATTER_WITHOUT_ZONEID))) + .build() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the datetime of ZoneId Z2 + assertEquals("\"" + date.withZoneSameInstant(Z2).format(FORMATTER_WITHOUT_ZONEID) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOffAndACustomFormatter() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + // With a custom DateTimeFormatter without a Zone. + String value = newMapperBuilder().addModule( + new SimpleModule().addSerializer(new ZonedDateTimeSerializer(FORMATTER_WITHOUT_ZONEID))) + .build() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the datetime of ZoneId Z3 + assertEquals("\"" + date.format(FORMATTER_WITHOUT_ZONEID) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOn() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z2 + assertEquals("\"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(date.withZoneSameInstant(Z2)) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOff() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z3 + assertEquals("\"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(date) + "\"", value); + } + + @Test + public void testSerializationWithTypeInfo01() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build() + .writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("[\"" + ZonedDateTime.class.getName() + "\",123456789.183917322]", value); + } + + @Test + public void testSerializationWithTypeInfo02() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + String value = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build() + .writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(date); + assertEquals("[\"" + ZonedDateTime.class.getName() + "\",123456789183]", value); + } + + @Test + public void testSerializationWithTypeInfo03() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = mapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build() + .writeValueAsString(date); + assertEquals("[\"" + ZonedDateTime.class.getName() + "\",\"" + + FORMATTER.withZone(Z3).format(date) + "\"]", value); + } + + @Test + public void testSerializationWithTypeInfoAndMapperTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build() + .writer() + .with(TimeZone.getTimeZone(Z3)) + .writeValueAsString(date); + assertEquals("[\"" + ZonedDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value); + } + + @Test + public void testDeserializationAsFloat01WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = MAPPER.readValue("0.000000000", ZonedDateTime.class); + + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat01WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = MAPPER + .readerFor(ZonedDateTime.class) + .with(TimeZone.getDefault()) + .readValue("0.000000000"); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat02WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ZonedDateTime value = MAPPER.readValue("123456789.183917322", ZonedDateTime.class); + + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat02WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ZonedDateTime value = MAPPER + .readerFor(ZonedDateTime.class) + .with(TimeZone.getDefault()) + .readValue("123456789.183917322"); + + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat03WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapper(); + ZonedDateTime value = mapper.readValue( + DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano()), ZonedDateTime.class + ); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsFloat03WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readValue( + DecimalUtils.toDecimal(date.toEpochSecond(), date.getNano()), ZonedDateTime.class + ); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01NanosecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = READER + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01NanosecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = newMapper(TimeZone.getDefault()).readerFor(ZonedDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01MillisecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = READER + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt01MillisecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = MAPPER_WITH_DEFAULT_TZ + .readerFor(ZonedDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("0"); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02NanosecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + ZonedDateTime value = MAPPER.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789"); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02NanosecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + ZonedDateTime value = MAPPER_WITH_DEFAULT_TZ.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789"); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02MillisecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + ZonedDateTime value = MAPPER.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789422"); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt02MillisecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("123456789422"); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03NanosecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + ObjectMapper mapper = newMapper(); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toEpochSecond())); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03NanosecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + date = date.minus(date.getNano(), ChronoUnit.NANOS); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toEpochSecond())); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03MillisecondsWithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + date = date.minus(date.getNano() - (date.get(ChronoField.MILLI_OF_SECOND) * 1_000_000L), ChronoUnit.NANOS); + ObjectMapper mapper = newMapper(); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toInstant().toEpochMilli())); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsInt03MillisecondsWithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + date = date.minus(date.getNano() - (date.get(ChronoField.MILLI_OF_SECOND) * 1_000_000L), ChronoUnit.NANOS); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue(Long.toString(date.toInstant().toEpochMilli())); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ObjectMapper mapper = newMapper(); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithTimeZoneTurnedOff() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), FIX_OFFSET); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(FIX_OFFSET, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString01WithZoneId() throws Exception { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), Z1); + ZonedDateTime value = MAPPER.readerFor(ZonedDateTime.class).readValue( + "\"" + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(date) + "\""); + assertIsEqual(date, value); + } + + @Test + public void testDeserializationAsString02WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ZonedDateTime value = READER + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ZonedDateTime value = newMapper(TimeZone.getDefault()) + .readerFor(ZonedDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithTimeZoneTurnedOff() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), FIX_OFFSET); + ObjectMapper mapper = newMapper(TimeZone.getDefault()); + ZonedDateTime value = mapper.readerFor(ZonedDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(FIX_OFFSET, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString02WithZoneId() throws Exception { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ZonedDateTime value = READER + .readValue("\"" + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(date) + "\""); + assertIsEqual(date, value); + } + + @Test + public void testDeserializationAsString03WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ZonedDateTime value = READER + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(DEFAULT_TZ, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ZonedDateTime value = newMapper(TimeZone.getDefault()) + .readerFor(ZonedDateTime.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(ZoneId.systemDefault().normalized(), value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithTimeZoneTurnedOff() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(FIX_OFFSET); + ZonedDateTime value = newMapper(TimeZone.getDefault()) + .readerFor(ZonedDateTime.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue('"' + FORMATTER.format(date) + '"'); + assertIsEqual(date, value); + assertEquals(FIX_OFFSET, value.getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationAsString03WithZoneId() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + ZonedDateTime value = MAPPER.readValue("\"" + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(date) + "\"", ZonedDateTime.class); + assertIsEqual(date, value); + } + + @Test + public void testDeserializationWithTypeInfo01WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue( + "[\"" + ZonedDateTime.class.getName() + "\",123456789.183917322]", Temporal.class + ); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(DEFAULT_TZ, ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo01WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 183917322), Z2); + ObjectMapper mapper = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readValue( + "[\"" + ZonedDateTime.class.getName() + "\",123456789.183917322]", Temporal.class + ); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(ZoneId.systemDefault().normalized(), ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + ZonedDateTime.class.getName() + "\",123456789]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(DEFAULT_TZ, ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo02WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 0), Z2); + ObjectMapper mapper = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .with(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + ZonedDateTime.class.getName() + "\",123456789]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(ZoneId.systemDefault().normalized(), ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper.readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue("[\"" + ZonedDateTime.class.getName() + "\",123456789422]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(DEFAULT_TZ, ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo03WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(123456789L, 422000000), Z2); + ObjectMapper mapper = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper + .readerFor(Temporal.class) + .without(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS) + .readValue( + "[\"" + ZonedDateTime.class.getName() + "\",123456789422]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(ZoneId.systemDefault().normalized(), ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithoutTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapperBuilder() + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper + .readerFor(Temporal.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + ZonedDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(DEFAULT_TZ, ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithTimeZone() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(Z3); + ObjectMapper mapper = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build(); + Temporal value = mapper + .readerFor(Temporal.class) + .with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + ZonedDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(ZoneId.systemDefault().normalized(), ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testDeserializationWithTypeInfo04WithTimeZoneTurnedOff() throws Exception + { + ZonedDateTime date = ZonedDateTime.now(FIX_OFFSET); + Temporal value = newMapperBuilder(TimeZone.getDefault()) + .addMixIn(Temporal.class, MockObjectConfiguration.class) + .build() + .readerFor(Temporal.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue( + "[\"" + ZonedDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]"); + assertInstanceOf(ZonedDateTime.class, value, "The value should be an ZonedDateTime."); + assertIsEqual(date, (ZonedDateTime) value); + assertEquals(FIX_OFFSET, ((ZonedDateTime) value).getZone(), "The time zone is not correct."); + } + + @Test + public void testCustomPatternWithAnnotations() throws Exception + { + ZonedDateTime inputValue = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), UTC); + final Wrapper input = new Wrapper(inputValue); + String json = MAPPER.writeValueAsString(input); + assertEquals(a2q("{'value':'1970_01_01 00:00:00(+0000)'}"), json); + + Wrapper result = MAPPER.readerFor(Wrapper.class) + .without(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .readValue(json); + // looks like timezone gets converted (is that correct or not?); verify just offsets for now + assertEquals(input.value.toInstant(), result.value.toInstant()); + } + + // [modules-java#269] + @Test + public void testCustomPatternWithNumericTimestamp() throws Exception + { + String input = a2q("{'value':'3.141592653'}"); + + Wrapper result = JsonMapper.builder() + .addModule(new JavaTimeModule() + .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + .build() + .readerFor(Wrapper.class) + .readValue(input); + + assertEquals(Instant.ofEpochSecond(3L, 141592653L), result.value.toInstant()); + } + + @Test + public void testNumericCustomPatternWithAnnotations() throws Exception + { + ZonedDateTime inputValue = ZonedDateTime.ofInstant(Instant.ofEpochSecond(0L), UTC); + final WrapperNumeric input = new WrapperNumeric(inputValue); + ObjectMapper m = newMapper(); + String json = m.writeValueAsString(input); + assertEquals(a2q("{'value':'19700101000000'}"), json); + + WrapperNumeric result = m.readValue(json, WrapperNumeric.class); + assertEquals(input.value.toInstant(), result.value.toInstant()); + } + + @Test + public void testInstantPriorToEpochIsEqual() throws Exception + { + //Issue #120 test + final Instant original = Instant.ofEpochMilli(-1); + final String serialized = MAPPER.writeValueAsString(original); + final Instant deserialized = MAPPER.readValue(serialized, Instant.class); + assertEquals(original, deserialized); + } + + public static class Pojo1 { + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) + public ZonedDateTime t1 = ZonedDateTime.parse("2022-04-27T12:00:00+02:00[Europe/Paris]"); + + public ZonedDateTime t2 = t1; + } + + @Test + public void testShapeInt() throws Exception { + String json1 = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build() + .writeValueAsString(new Pojo1()); + assertEquals("{\"t1\":1651053600000,\"t2\":1651053600.000000000}", json1); + } + + private static void assertIsEqual(ZonedDateTime expected, ZonedDateTime actual) + { + assertTrue(expected.isEqual(actual), + "The value is not correct. Expected timezone-adjusted <" + expected + ">, actual <" + actual + ">."); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java new file mode 100644 index 0000000000..3b2d1203f4 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java @@ -0,0 +1,43 @@ +package tools.jackson.databind.datetime.ser; + +import java.time.ZonedDateTime; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [module-java8#333]: ZonedDateTime serialization with @JsonFormat pattern never uses +// while WRITE_DATES_WITH_ZONE_ID enabled #333 +public class ZonedDateTimeSerWithJsonFormat333Test + extends ModuleTestBase +{ + public static class ContainerWithPattern333 { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z") + public ZonedDateTime value; + } + + public static class ContainerWithoutPattern333 { + @JsonFormat(shape = JsonFormat.Shape.STRING) + public ZonedDateTime value; + } + + private final ObjectMapper MAPPER = mapperBuilder().enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID).build(); + + @Test + public void testJsonFormatOverridesSerialization() throws Exception + { + // ISO-8601 string for ZonedDateTime + ZonedDateTime zonedDateTime = ZonedDateTime.parse("2024-11-15T18:27:06.921054+01:00[Europe/Berlin]"); + ContainerWithPattern333 input = new ContainerWithPattern333(); + input.value = zonedDateTime; + + assertEquals(a2q("{'value':'2024-11-15 18:27:06 CET'}"), + MAPPER.writeValueAsString(input)); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/tofix/InstantDeserializerNegative359Test.java b/src/test/java/tools/jackson/databind/datetime/tofix/InstantDeserializerNegative359Test.java new file mode 100644 index 0000000000..a6dbde19ea --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/tofix/InstantDeserializerNegative359Test.java @@ -0,0 +1,39 @@ +package tools.jackson.databind.datetime.tofix; + +import java.time.Instant; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +// [modules-java8#359] InstantDeserializer deserializes the nanosecond portion of +// fractional timestamps incorrectly: -1.000000001 deserializes to 1969-12-31T23:59:59.000000001Z +// instead of 1969-12-31T23:59:58.999999999Z +public class InstantDeserializerNegative359Test + extends ModuleTestBase +{ + private final ObjectReader READER = newMapper().readerFor(Instant.class); + + @JacksonTestFailureExpected + @Test + public void testDeserializationAsFloat04() + throws Exception + { + Instant actual = READER.readValue("-1.000000001"); + Instant expected = Instant.ofEpochSecond(-1L, -1L); + assertEquals(expected, actual); + } + + @Test + public void testDeserializationAsFloat05() + throws Exception + { + Instant actual = READER.readValue("-0.000000001"); + Instant expected = Instant.ofEpochSecond(0L, -1L); + assertEquals(expected, actual); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/tofix/InstantViaBigDecimal307Test.java b/src/test/java/tools/jackson/databind/datetime/tofix/InstantViaBigDecimal307Test.java new file mode 100644 index 0000000000..6cb2b5246c --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/tofix/InstantViaBigDecimal307Test.java @@ -0,0 +1,47 @@ +package tools.jackson.databind.datetime.tofix; + +import java.time.Instant; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.*; + +// [modules-java8#307]: Loss of precision via JsonNode for BigDecimal-valued +// things (like Instant) +public class InstantViaBigDecimal307Test extends ModuleTestBase +{ + public static class Wrapper307 { + public Instant value; + + public Wrapper307(Instant v) { value = v; } + public Wrapper307() { } + } + + private final Instant ISSUED_AT = Instant.ofEpochSecond(1234567890).plusNanos(123456789); + + private ObjectMapper MAPPER = mapperBuilder() + .enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + @Test + public void instantViaReadValue() throws Exception { + String serialized = MAPPER.writeValueAsString(new Wrapper307(ISSUED_AT)); + Wrapper307 deserialized = MAPPER.readValue(serialized, Wrapper307.class); + assertEquals(ISSUED_AT, deserialized.value); + } + + @JacksonTestFailureExpected + @Test + public void instantViaReadTree() throws Exception { + String serialized = MAPPER.writeValueAsString(new Wrapper307(ISSUED_AT)); + JsonNode tree = MAPPER.readTree(serialized); + Wrapper307 deserialized = MAPPER.treeToValue(tree, Wrapper307.class); + assertEquals(ISSUED_AT, deserialized.value); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/tofix/OffsetDateTimeDeser279Test.java b/src/test/java/tools/jackson/databind/datetime/tofix/OffsetDateTimeDeser279Test.java new file mode 100644 index 0000000000..baddeb5650 --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/tofix/OffsetDateTimeDeser279Test.java @@ -0,0 +1,50 @@ +package tools.jackson.databind.datetime.tofix; + +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.*; + +public class OffsetDateTimeDeser279Test extends ModuleTestBase +{ + // For [modules-java8#279] + static class Wrapper279 { + OffsetDateTime date; + + public Wrapper279(OffsetDateTime d) { date = d; } + protected Wrapper279() { } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + public OffsetDateTime getDate() { + return date; + } + public void setDate(OffsetDateTime date) { + this.date = date; + } + } + + private ObjectMapper MAPPER = newMapper(); + + // For [modules-java8#279] + @JacksonTestFailureExpected + @Test + public void testWrapperWithPattern279() throws Exception + { + final OffsetDateTime date = OffsetDateTime.now(ZoneId.of("UTC")) + .truncatedTo(ChronoUnit.SECONDS); + final Wrapper279 input = new Wrapper279(date); + final String json = MAPPER.writeValueAsString(input); + + Wrapper279 result = MAPPER.readValue(json, Wrapper279.class); + assertEquals(input.date, result.date); + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/tofix/ZonedDateTimeIssue244Test.java b/src/test/java/tools/jackson/databind/datetime/tofix/ZonedDateTimeIssue244Test.java new file mode 100644 index 0000000000..5cee21b18b --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/tofix/ZonedDateTimeIssue244Test.java @@ -0,0 +1,72 @@ +package tools.jackson.databind.datetime.tofix; + +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.datetime.ModuleTestBase; +import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test case for https://github.com/FasterXML/jackson-modules-java8/issues/244 + */ +public class ZonedDateTimeIssue244Test extends ModuleTestBase +{ + private final ObjectMapper MAPPER = mapperBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + @JacksonTestFailureExpected + @Test + public void zoneIdUTC() throws Exception + { + assertSerializeAndDeserialize(ZonedDateTime.now(ZoneId.of("UTC"))); + } + + @Test + public void zoneOffsetUTC() throws Exception + { + assertSerializeAndDeserialize(ZonedDateTime.now(ZoneOffset.UTC)); // fails! + } + + @JacksonTestFailureExpected + @Test + public void zoneOffsetNonUTC() throws Exception + { + assertSerializeAndDeserialize(ZonedDateTime.now(ZoneOffset.ofHours(-7))); // fails! + } + + private void assertSerializeAndDeserialize(final ZonedDateTime date) throws Exception + { + final Example example1 = new Example(date); + final String json = MAPPER.writeValueAsString(example1); + final Example example2 = MAPPER.readValue(json, Example.class); + + assertEquals(example1.getDate(), example2.getDate()); + } + + static class Example + { + private ZonedDateTime date; + + public Example() + { + } + + public Example(final ZonedDateTime date) + { + this.date = date; + } + + public ZonedDateTime getDate() + { + return date; + } + } +} diff --git a/src/test/java/tools/jackson/databind/datetime/util/DurationUnitConverterTest.java b/src/test/java/tools/jackson/databind/datetime/util/DurationUnitConverterTest.java new file mode 100644 index 0000000000..dad6fafc1c --- /dev/null +++ b/src/test/java/tools/jackson/databind/datetime/util/DurationUnitConverterTest.java @@ -0,0 +1,53 @@ +package tools.jackson.databind.datetime.util; + +import java.time.temporal.ChronoUnit; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.datetime.ModuleTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +public class DurationUnitConverterTest + extends ModuleTestBase +{ + @Test + public void shouldMapToTemporalUnit() { + for (ChronoUnit inputUnit : new ChronoUnit[] { + ChronoUnit.NANOS, + ChronoUnit.MICROS, + ChronoUnit.MILLIS, + ChronoUnit.SECONDS, + ChronoUnit.MINUTES, + ChronoUnit.HOURS, + ChronoUnit.HALF_DAYS, + ChronoUnit.DAYS, + }) { + DurationUnitConverter conv = DurationUnitConverter.from(inputUnit.name()); + assertNotNull(conv); + // is case-sensitive: + assertNull(DurationUnitConverter.from(inputUnit.name().toLowerCase())); + } + } + + @Test + public void shouldNotMapToTemporalUnit() { + for (String invalid : new String[] { + // Inaccurate units not (yet?) supported + "WEEKS", + "MONTHS", + "YEARS", + "DECADES", + "CENTURIES", + "MILLENNIA", + "ERAS", + "FOREVER", + + // Not matching at all + "DOESNOTMATCH", "", " " + }) { + assertNull(DurationUnitConverter.from(invalid), + "Should not map pattern '"+invalid+"'"); + } + } +} From b25ee2514653481ca7b7516f7421f0fd5112e641 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 11:16:57 +0100 Subject: [PATCH 03/21] move javatimefeature enable to mapperbuilder --- .../tools/jackson/databind/JacksonModule.java | 5 ++++ .../jackson/databind/cfg/MapperBuilder.java | 18 ++++++++++++- .../databind/cfg/ModuleContextBase.java | 12 +++++++++ .../databind/datetime/JavaTimeModule.java | 25 +++++-------------- .../jackson/databind/json/JsonMapper.java | 15 +++++++++++ .../datetime/deser/InstantDeser291Test.java | 4 +-- .../datetime/deser/LocalDateDeserTest.java | 3 ++- .../deser/LocalDateTimeDeserTest.java | 3 ++- .../deser/OneBasedMonthDeserTest.java | 10 +++++--- .../deser/ZonedDateTimeDeserTest.java | 3 ++- .../datetime/ser/OneBasedMonthSerTest.java | 6 +++-- .../datetime/ser/ZonedDateTimeSerTest.java | 4 +-- 12 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/main/java/tools/jackson/databind/JacksonModule.java b/src/main/java/tools/jackson/databind/JacksonModule.java index b279aa2900..c9eec09f6f 100644 --- a/src/main/java/tools/jackson/databind/JacksonModule.java +++ b/src/main/java/tools/jackson/databind/JacksonModule.java @@ -5,8 +5,10 @@ import java.util.function.UnaryOperator; import tools.jackson.core.*; +import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.cfg.MapperBuilder; import tools.jackson.databind.cfg.MutableConfigOverride; +import tools.jackson.databind.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.Serializers; @@ -146,6 +148,9 @@ public static interface SetupContext public boolean isEnabled(TokenStreamFactory.Feature f); public boolean isEnabled(StreamReadFeature f); public boolean isEnabled(StreamWriteFeature f); + public boolean isEnabled(JavaTimeFeature f); + + public JacksonFeatureSet getJavaTimeFeatures(); /* /****************************************************************** diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index b61652d35f..2d6cf41e74 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -13,8 +13,10 @@ import tools.jackson.core.*; import tools.jackson.core.util.DefaultPrettyPrinter; +import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; +import tools.jackson.databind.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.introspect.*; import tools.jackson.databind.jsontype.*; @@ -199,7 +201,7 @@ public abstract class MapperBuilder _javaTimeFeatures = + JacksonFeatureSet.fromDefaults(JavaTimeFeature.values()); + /* /********************************************************************** /* Transient state @@ -493,6 +501,14 @@ public boolean isEnabled(StreamWriteFeature f) { return f.enabledIn(_streamWriteFeatures); } + public boolean isEnabled(JavaTimeFeature f) { + return _javaTimeFeatures.isEnabled(f); + } + + public JacksonFeatureSet getJavaTimeFeatures() { + return _javaTimeFeatures; + } + public DatatypeFeatures datatypeFeatures() { return _datatypeFeatures; } diff --git a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java index 7476564126..73242fcb4d 100644 --- a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java +++ b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java @@ -4,8 +4,10 @@ import java.util.function.UnaryOperator; import tools.jackson.core.*; +import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.*; import tools.jackson.databind.JacksonModule.SetupContext; +import tools.jackson.databind.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.SerializerFactory; @@ -125,6 +127,16 @@ public boolean isEnabled(StreamWriteFeature f) { return _builder.isEnabled(f); } + @Override + public boolean isEnabled(JavaTimeFeature f) { + return _builder.isEnabled(f); + } + + @Override + public JacksonFeatureSet getJavaTimeFeatures() { + return _builder.getJavaTimeFeatures(); + } + /* /********************************************************************** /* Mutators for adding deserializers, related diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java index d9039e1899..5722a49076 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java @@ -87,9 +87,6 @@ public final class JavaTimeModule { private static final long serialVersionUID = 1L; - private JacksonFeatureSet _features - = JacksonFeatureSet.fromDefaults(JavaTimeFeature.values()); - @Override public String getModuleName() { return getClass().getName(); @@ -102,33 +99,23 @@ public Version version() { public JavaTimeModule() { } - public JavaTimeModule enable(JavaTimeFeature f) { - _features = _features.with(f); - return this; - } - - public JavaTimeModule disable(JavaTimeFeature f) { - _features = _features.without(f); - return this; - } - @Override public void setupModule(SetupContext context) { context.addDeserializers(new SimpleDeserializers() // // Instant variants: .addDeserializer(Instant.class, - InstantDeserializer.INSTANT.withFeatures(_features)) + InstantDeserializer.INSTANT.withFeatures(context.getJavaTimeFeatures())) .addDeserializer(OffsetDateTime.class, - InstantDeserializer.OFFSET_DATE_TIME.withFeatures(_features)) + InstantDeserializer.OFFSET_DATE_TIME.withFeatures(context.getJavaTimeFeatures())) .addDeserializer(ZonedDateTime.class, - InstantDeserializer.ZONED_DATE_TIME.withFeatures(_features)) + InstantDeserializer.ZONED_DATE_TIME.withFeatures(context.getJavaTimeFeatures())) // // Other deserializers .addDeserializer(Duration.class, DurationDeserializer.INSTANCE) .addDeserializer(LocalDateTime.class, - LocalDateTimeDeserializer.INSTANCE.withFeatures(_features)) + LocalDateTimeDeserializer.INSTANCE.withFeatures(context.getJavaTimeFeatures())) .addDeserializer(LocalDate.class, - LocalDateDeserializer.INSTANCE.withFeatures(_features)) + LocalDateDeserializer.INSTANCE.withFeatures(context.getJavaTimeFeatures())) .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE) .addDeserializer(MonthDay.class, MonthDayDeserializer.INSTANCE) .addDeserializer(OffsetTime.class, OffsetTimeDeserializer.INSTANCE) @@ -186,7 +173,7 @@ public void setupModule(SetupContext context) { ); // [modules-java8#274]: 1-based Month (de)serializer need to be applied via modifiers: - final boolean oneBasedMonthEnabled = _features.isEnabled(JavaTimeFeature.ONE_BASED_MONTHS); + final boolean oneBasedMonthEnabled = context.isEnabled(JavaTimeFeature.ONE_BASED_MONTHS); context.addDeserializerModifier(new JavaTimeDeserializerModifier(oneBasedMonthEnabled)); context.addSerializerModifier(new JavaTimeSerializerModifier(oneBasedMonthEnabled)); diff --git a/src/main/java/tools/jackson/databind/json/JsonMapper.java b/src/main/java/tools/jackson/databind/json/JsonMapper.java index 7bdb122bf1..57bf4c2db0 100644 --- a/src/main/java/tools/jackson/databind/json/JsonMapper.java +++ b/src/main/java/tools/jackson/databind/json/JsonMapper.java @@ -8,6 +8,7 @@ import tools.jackson.databind.cfg.MapperBuilder; import tools.jackson.databind.cfg.MapperBuilderState; import tools.jackson.databind.cfg.PackageVersion; +import tools.jackson.databind.datetime.JavaTimeFeature; /** * JSON-specific {@link ObjectMapper} implementation. @@ -90,6 +91,20 @@ public Builder disable(JsonWriteFeature... features) { return this; } + public Builder enable(JavaTimeFeature... features) { + for (JavaTimeFeature f : features) { + _javaTimeFeatures = _javaTimeFeatures.with(f); + } + return this; + } + + public Builder disable(JavaTimeFeature... features) { + for (JavaTimeFeature f : features) { + _javaTimeFeatures = _javaTimeFeatures.without(f); + } + return this; + } + public Builder configure(JsonWriteFeature feature, boolean state) { if (state) { diff --git a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java index 6c2458750f..577d3ce317 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java @@ -22,8 +22,8 @@ public class InstantDeser291Test { private final JsonMapper MAPPER = JsonMapper.builder() .defaultLocale(Locale.ENGLISH) - .addModule(new JavaTimeModule() - .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build(); private final ObjectReader READER = MAPPER.readerFor(Instant.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java index 9209cbfd69..0677934878 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java @@ -34,7 +34,8 @@ public class LocalDateDeserTest extends ModuleTestBase private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(LocalDate.class); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .addModule(new JavaTimeModule().enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDate.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java index 98a46d0ed0..6dbc02f645 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java @@ -58,7 +58,8 @@ public class LocalDateTimeDeserTest .build(); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .addModule(new JavaTimeModule().enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDateTime.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java index b1f16a9a99..b2e4c36445 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java @@ -108,7 +108,8 @@ public void testDeserialization02_oneBased() throws Exception public void testDeserializationWithTypeInfo01_oneBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() - .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.ONE_BASED_MONTHS) .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) .build(); @@ -178,15 +179,16 @@ public void testDeserializeFromEmptyString() throws Exception private ObjectReader readerForZeroBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule() - .disable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addModule(new JavaTimeModule()) + .disable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); } private ObjectReader readerForOneBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); } diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java index 0ce85b132e..6df1bda57b 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java @@ -29,7 +29,8 @@ public class ZonedDateTimeDeserTest extends ModuleTestBase private final ObjectReader READER = newMapper().readerFor(ZonedDateTime.class); private final ObjectReader READER_NON_NORMALIZED_ZONEID = JsonMapper.builder() - .addModule(new JavaTimeModule().disable(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) + .addModule(new JavaTimeModule()) + .disable(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID) .build() .readerFor(ZonedDateTime.class); diff --git a/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java index a4e4ae5be7..1abc98e062 100644 --- a/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java @@ -51,14 +51,16 @@ public void testSerializationFromEnumWithPattern_zeroBased() throws Exception private ObjectWriter writerForZeroBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule().disable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addModule(new JavaTimeModule()) + .disable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .writer(); } private ObjectWriter writerForOneBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule().enable(JavaTimeFeature.ONE_BASED_MONTHS)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .writer(); } diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java index 6b1b73828f..34d5053f8d 100644 --- a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java @@ -912,8 +912,8 @@ public void testCustomPatternWithNumericTimestamp() throws Exception String input = a2q("{'value':'3.141592653'}"); Wrapper result = JsonMapper.builder() - .addModule(new JavaTimeModule() - .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + .addModule(new JavaTimeModule()) + .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build() .readerFor(Wrapper.class) .readValue(input); From 615f6f24f55b650daa4f6cf66d8e0bd0616e0667 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 11:21:14 +0100 Subject: [PATCH 04/21] accidental edit (fixed) --- src/main/java/tools/jackson/databind/cfg/MapperBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index 2d6cf41e74..85694e7d20 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -201,7 +201,7 @@ public abstract class MapperBuilder Date: Tue, 18 Mar 2025 11:37:24 +0100 Subject: [PATCH 05/21] auto-enable JavaTimeModule - breaks a few tests --- .../tools/jackson/databind/cfg/MapperBuilder.java | 2 ++ .../jackson/databind/datetime/JavaTimeModule.java | 14 ++++++++------ .../jackson/databind/datetime/ModuleTestBase.java | 9 +++------ .../datetime/deser/InstantDeser291Test.java | 4 ---- .../datetime/deser/LocalDateDeserTest.java | 2 -- .../datetime/deser/LocalDateTimeDeserTest.java | 2 -- .../datetime/deser/OneBasedMonthDeserTest.java | 4 ---- .../databind/datetime/deser/ZoneIdDeserTest.java | 2 -- .../datetime/deser/ZonedDateTimeDeserTest.java | 2 -- .../datetime/ser/OneBasedMonthSerTest.java | 3 --- .../databind/datetime/ser/WriteZoneIdTest.java | 2 -- .../datetime/ser/ZonedDateTimeSerTest.java | 2 -- 12 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index 85694e7d20..8c98b3f4b9 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -17,6 +17,7 @@ import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.deser.*; import tools.jackson.databind.introspect.*; import tools.jackson.databind.jsontype.*; @@ -308,6 +309,7 @@ protected MapperBuilder(TokenStreamFactory streamFactory) _abstractTypeResolvers = NO_ABSTRACT_TYPE_RESOLVERS; _defaultAttributes = null; + addModule(JavaTimeModule.getInstance()); } /** diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java index 5722a49076..cdfd780679 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java @@ -40,11 +40,8 @@ /** * Class that registers capability of serializing {@code java.time} objects with the Jackson core. - * - *
- * ObjectMapper mapper = new ObjectMapper();
- * mapper.registerModule(new JavaTimeModule());
- * 
+ *

+ * In Jackson 3, the module is automatically registered. *

* Most {@code java.time} types are serialized as numbers (integers or decimals as appropriate) if the * {@link tools.jackson.databind.SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} feature is enabled @@ -86,6 +83,7 @@ public final class JavaTimeModule implements java.io.Serializable { private static final long serialVersionUID = 1L; + private static final JavaTimeModule INSTANCE = new JavaTimeModule(); @Override public String getModuleName() { @@ -97,7 +95,11 @@ public Version version() { return PackageVersion.VERSION; } - public JavaTimeModule() { } + public static JavaTimeModule getInstance() { + return INSTANCE; + } + + private JavaTimeModule() { } @Override public void setupModule(SetupContext context) { diff --git a/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java b/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java index 41506ef54a..bc17e0a788 100644 --- a/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java +++ b/src/test/java/tools/jackson/databind/datetime/ModuleTestBase.java @@ -26,15 +26,13 @@ protected static ObjectMapper newMapper() { protected static MapperBuilder newMapperBuilder() { return JsonMapper.builder() - .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) - .addModule(new JavaTimeModule()); + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES); } protected static MapperBuilder newMapperBuilder(TimeZone tz) { return JsonMapper.builder() .defaultTimeZone(tz) - .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) - .addModule(new JavaTimeModule()); + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES); } protected static ObjectMapper newMapper(TimeZone tz) { @@ -44,8 +42,7 @@ protected static ObjectMapper newMapper(TimeZone tz) { protected static JsonMapper.Builder mapperBuilder() { return JsonMapper.builder() .defaultLocale(Locale.ENGLISH) - .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES) - .addModule(new JavaTimeModule()); + .disable(JsonWriteFeature.ESCAPE_FORWARD_SLASHES); } protected String q(String value) { diff --git a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java index 577d3ce317..29f268041e 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/InstantDeser291Test.java @@ -5,12 +5,9 @@ import org.junit.jupiter.api.Test; -import tools.jackson.core.json.JsonReadFeature; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.ModuleTestBase; import static org.junit.jupiter.api.Assertions.*; @@ -22,7 +19,6 @@ public class InstantDeser291Test { private final JsonMapper MAPPER = JsonMapper.builder() .defaultLocale(Locale.ENGLISH) - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build(); private final ObjectReader READER = MAPPER.readerFor(Instant.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java index 0677934878..c240ab2722 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateDeserTest.java @@ -23,7 +23,6 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; @@ -34,7 +33,6 @@ public class LocalDateDeserTest extends ModuleTestBase private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(LocalDate.class); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDate.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java index 6dbc02f645..2e86265cbb 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/LocalDateTimeDeserTest.java @@ -40,7 +40,6 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; @@ -58,7 +57,6 @@ public class LocalDateTimeDeserTest .build(); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDateTime.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java index b2e4c36445..3010b6655b 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/OneBasedMonthDeserTest.java @@ -14,7 +14,6 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; @@ -108,7 +107,6 @@ public void testDeserialization02_oneBased() throws Exception public void testDeserializationWithTypeInfo01_oneBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.ONE_BASED_MONTHS) .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) .build(); @@ -179,7 +177,6 @@ public void testDeserializeFromEmptyString() throws Exception private ObjectReader readerForZeroBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule()) .disable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); @@ -187,7 +184,6 @@ private ObjectReader readerForZeroBased() { private ObjectReader readerForOneBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java index 64188e8f8e..d6d5c746d0 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZoneIdDeserTest.java @@ -14,7 +14,6 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.type.LogicalType; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; @@ -43,7 +42,6 @@ public void testPolymorphicZoneIdDeser() { ObjectMapper mapper = JsonMapper.builder() .addMixIn(ZoneId.class, MockObjectConfiguration.class) - .addModule(new JavaTimeModule()) .build(); ZoneId value = mapper.readValue("[\"" + ZoneId.class.getName() + "\",\"America/Denver\"]", ZoneId.class); assertEquals(ZoneId.of("America/Denver"), value); diff --git a/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java index 6df1bda57b..eafe584543 100644 --- a/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/datetime/deser/ZonedDateTimeDeserTest.java @@ -19,7 +19,6 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.ModuleTestBase; import static org.junit.jupiter.api.Assertions.*; @@ -29,7 +28,6 @@ public class ZonedDateTimeDeserTest extends ModuleTestBase private final ObjectReader READER = newMapper().readerFor(ZonedDateTime.class); private final ObjectReader READER_NON_NORMALIZED_ZONEID = JsonMapper.builder() - .addModule(new JavaTimeModule()) .disable(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID) .build() .readerFor(ZonedDateTime.class); diff --git a/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java index 1abc98e062..9f7b3e353d 100644 --- a/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/datetime/ser/OneBasedMonthSerTest.java @@ -9,7 +9,6 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.ModuleTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,7 +50,6 @@ public void testSerializationFromEnumWithPattern_zeroBased() throws Exception private ObjectWriter writerForZeroBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule()) .disable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .writer(); @@ -59,7 +57,6 @@ private ObjectWriter writerForZeroBased() { private ObjectWriter writerForOneBased() { return JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.ONE_BASED_MONTHS) .build() .writer(); diff --git a/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java b/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java index ccb5605515..739f0ab7d5 100644 --- a/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java +++ b/src/test/java/tools/jackson/databind/datetime/ser/WriteZoneIdTest.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; @@ -55,7 +54,6 @@ public void testSerializationWithTypeInfo01() throws Exception ZoneId id = ZoneId.of("America/Denver"); ObjectMapper mapper = mapperBuilder() .addMixIn(ZoneId.class, MockObjectConfiguration.class) - .addModule(new JavaTimeModule()) .build(); String value = mapper.writeValueAsString(id); assertEquals("[\"java.time.ZoneId\",\"America/Denver\"]", value); diff --git a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java index 34d5053f8d..58572c9cf0 100644 --- a/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/datetime/ser/ZonedDateTimeSerTest.java @@ -35,7 +35,6 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; import tools.jackson.databind.datetime.MockObjectConfiguration; import tools.jackson.databind.datetime.ModuleTestBase; import tools.jackson.databind.datetime.util.DecimalUtils; @@ -912,7 +911,6 @@ public void testCustomPatternWithNumericTimestamp() throws Exception String input = a2q("{'value':'3.141592653'}"); Wrapper result = JsonMapper.builder() - .addModule(new JavaTimeModule()) .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build() .readerFor(Wrapper.class) From e586ec5c89ff53f48f6a09da7cf1ceea92c99fb6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 11:48:49 +0100 Subject: [PATCH 06/21] remove code that warns if java.time classes are used but module is not enabled --- .../tools/jackson/databind/util/BeanUtil.java | 24 +---- .../interop/DateJava8FallbacksTest.java | 101 ------------------ 2 files changed, 2 insertions(+), 123 deletions(-) delete mode 100644 src/test/java/tools/jackson/databind/interop/DateJava8FallbacksTest.java diff --git a/src/main/java/tools/jackson/databind/util/BeanUtil.java b/src/main/java/tools/jackson/databind/util/BeanUtil.java index b41f7d931a..557d716ac6 100644 --- a/src/main/java/tools/jackson/databind/util/BeanUtil.java +++ b/src/main/java/tools/jackson/databind/util/BeanUtil.java @@ -112,19 +112,7 @@ public static String checkUnsupportedType(MapperConfig config, JavaType type) final String className = type.getRawClass().getName(); String typeName, moduleName; - if (isJava8TimeClass(className)) { - // [modules-java8#207]: do NOT check/block helper types in sub-packages, - // but only main-level types (to avoid issues with module) - if (className.indexOf('.', 10) >= 0) { - return null; - } - // [databind#4718]: Also don't worry about Exception type(s) - if (type.isTypeOrSubTypeOf(Throwable.class)) { - return null; - } - typeName = "Java 8 date/time"; - moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"; - } else if (isJodaTimeClass(className)) { + if (isJodaTimeClass(className)) { typeName = "Joda date/time"; moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-joda"; } else { @@ -133,15 +121,7 @@ public static String checkUnsupportedType(MapperConfig config, JavaType type) return String.format("%s type %s not supported by default: add Module \"%s\" to enable handling", typeName, ClassUtil.getTypeDescription(type), moduleName); } - - public static boolean isJava8TimeClass(Class rawType) { - return isJava8TimeClass(rawType.getName()); - } - - private static boolean isJava8TimeClass(String className) { - return className.startsWith("java.time."); - } - + public static boolean isJodaTimeClass(Class rawType) { return isJodaTimeClass(rawType.getName()); } diff --git a/src/test/java/tools/jackson/databind/interop/DateJava8FallbacksTest.java b/src/test/java/tools/jackson/databind/interop/DateJava8FallbacksTest.java deleted file mode 100644 index 567d35cc9b..0000000000 --- a/src/test/java/tools/jackson/databind/interop/DateJava8FallbacksTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package tools.jackson.databind.interop; - -import java.time.DateTimeException; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; - -import org.junit.jupiter.api.Test; - -import tools.jackson.core.JsonParser; -import tools.jackson.databind.*; -import tools.jackson.databind.exc.InvalidDefinitionException; -import tools.jackson.databind.testutil.DatabindTestUtil; -import tools.jackson.databind.util.TokenBuffer; - -import static org.junit.jupiter.api.Assertions.*; - -// [databind#2683]: add fallback handling for Java 8 date/time types, to -// prevent accidental serialization as POJOs, as well as give more information -// on deserialization attempts -// -// @since 2.12 -public class DateJava8FallbacksTest extends DatabindTestUtil -{ - private final ObjectMapper MAPPER = newJsonMapper(); - - private final OffsetDateTime DATETIME_EPOCH = OffsetDateTime.ofInstant(Instant.ofEpochSecond(0L), - ZoneOffset.of("Z")); - - // Test to prevent serialization as POJO, without Java 8 date/time module: - @Test - public void testPreventSerialization() throws Exception - { - try { - String json = MAPPER.writerWithDefaultPrettyPrinter() - .writeValueAsString(DATETIME_EPOCH); - fail("Should not pass, wrote out as\n: "+json); - } catch (InvalidDefinitionException e) { - verifyException(e, "Java 8 date/time type `java.time.OffsetDateTime` not supported by default"); - verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\""); - } - } - - @Test - public void testBetterDeserializationError() throws Exception - { - try { - OffsetDateTime result = MAPPER.readValue(" 0 ", OffsetDateTime.class); - fail("Not expecting to pass, resulted in: "+result); - } catch (InvalidDefinitionException e) { - verifyException(e, "Java 8 date/time type `java.time.OffsetDateTime` not supported by default"); - verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\""); - } - } - - // But, [databind#3091], allow deser from JsonToken.VALUE_EMBEDDED_OBJECT - @Test - public void testAllowAsEmbedded() throws Exception - { - OffsetDateTime time = OffsetDateTime.ofInstant(Instant.now(), - ZoneId.of("Z")); - try (TokenBuffer tb = TokenBuffer.forGeneration()) { - tb.writeEmbeddedObject(time); - - try (JsonParser p = tb.asParser()) { - OffsetDateTime result = MAPPER.readValue(p, OffsetDateTime.class); - assertSame(time, result); - } - } - - // but also try deser into an array - try (TokenBuffer tb = TokenBuffer.forGeneration()) { - tb.writeStartArray(); - tb.writeEmbeddedObject(time); - tb.writeEndArray(); - - try (JsonParser p = tb.asParser()) { - Object[] result = MAPPER.readValue(p, Object[].class); - assertNotNull(result); - assertEquals(1, result.length); - assertSame(time, result[0]); - } - } - } - - // [databind#4718]: should not block serialization of `DateTimeException` - @Test - public void testAllowExceptionSer() throws Exception { - String json = MAPPER.writeValueAsString(new DateTimeException("Test!")); - assertTrue(MAPPER.readTree(json).isObject()); - } - - // [databind#4718]: should not block deserialization of `DateTimeException` - @Test - public void testAllowExceptionDeser() throws Exception { - DateTimeException exc = MAPPER.readValue("{\"message\":\"test!\"}", - DateTimeException.class); - assertNotNull(exc); - } -} From 70d31945f59a9300521830ea29d546702f341bb1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 14:07:33 +0100 Subject: [PATCH 07/21] remove JavaTimeModule --- .../jackson/databind/cfg/MapperBuilder.java | 6 ++--- .../databind/datetime/JavaTimeFeature.java | 2 +- ...meModule.java => JavaTimeInitializer.java} | 25 ++++--------------- 3 files changed, 9 insertions(+), 24 deletions(-) rename src/main/java/tools/jackson/databind/datetime/{JavaTimeModule.java => JavaTimeInitializer.java} (95%) diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index 8c98b3f4b9..a8dd49040f 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -17,7 +17,7 @@ import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeModule; +import tools.jackson.databind.datetime.JavaTimeInitializer; import tools.jackson.databind.deser.*; import tools.jackson.databind.introspect.*; import tools.jackson.databind.jsontype.*; @@ -309,7 +309,6 @@ protected MapperBuilder(TokenStreamFactory streamFactory) _abstractTypeResolvers = NO_ABSTRACT_TYPE_RESOLVERS; _defaultAttributes = null; - addModule(JavaTimeModule.getInstance()); } /** @@ -429,8 +428,9 @@ public MapperBuilderState saveStateApplyModules() { if (_savedState == null) { _savedState = _saveState(); + ModuleContextBase ctxt = _constructModuleContext(); + JavaTimeInitializer.getInstance().setupModule(ctxt); if (_modules != null) { - ModuleContextBase ctxt = _constructModuleContext(); _modules.values().forEach(m -> m.setupModule(ctxt)); // and since context may buffer some changes, ensure those are flushed: ctxt.applyChanges(this); diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java index 66f699baf5..1c6393aa8a 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java @@ -3,7 +3,7 @@ import tools.jackson.core.util.JacksonFeature; /** - * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeModule}). + * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). * * @since 2.16 */ diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java similarity index 95% rename from src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java rename to src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java index cdfd780679..749fc23671 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeModule.java +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java @@ -18,9 +18,6 @@ import java.time.*; -import tools.jackson.core.Version; -import tools.jackson.core.json.PackageVersion; -import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.*; import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; @@ -78,31 +75,19 @@ * * @since 2.6 */ -public final class JavaTimeModule - extends JacksonModule +public final class JavaTimeInitializer implements java.io.Serializable { private static final long serialVersionUID = 1L; - private static final JavaTimeModule INSTANCE = new JavaTimeModule(); + private static final JavaTimeInitializer INSTANCE = new JavaTimeInitializer(); - @Override - public String getModuleName() { - return getClass().getName(); - } - - @Override - public Version version() { - return PackageVersion.VERSION; - } - - public static JavaTimeModule getInstance() { + public static JavaTimeInitializer getInstance() { return INSTANCE; } - private JavaTimeModule() { } + private JavaTimeInitializer() { } - @Override - public void setupModule(SetupContext context) { + public void setupModule(JacksonModule.SetupContext context) { context.addDeserializers(new SimpleDeserializers() // // Instant variants: .addDeserializer(Instant.class, From d47a0e9664fc51e50bdbfd04134c98149b052f5f Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 14:23:54 +0100 Subject: [PATCH 08/21] fix test - no longer fails because java.time is now supported out of the box --- .../java/tools/jackson/databind/node/POJONodeTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/java/tools/jackson/databind/node/POJONodeTest.java b/src/test/java/tools/jackson/databind/node/POJONodeTest.java index 09b96dab2c..ee7176cd1c 100644 --- a/src/test/java/tools/jackson/databind/node/POJONodeTest.java +++ b/src/test/java/tools/jackson/databind/node/POJONodeTest.java @@ -65,15 +65,13 @@ public void testPOJONodeCustomSer() throws Exception @Test public void testAddJava8DateAsPojo() throws Exception { - JsonNode node = MAPPER.createObjectNode().putPOJO("test", LocalDateTime.now()); + LocalDateTime dt = LocalDateTime.now(); + JsonNode node = MAPPER.createObjectNode().putPOJO("test", dt); String json = node.toString(); assertNotNull(json); JsonNode result = MAPPER.readTree(json); String msg = result.path("test").asString(); - assertTrue(msg.startsWith("[ERROR:"), - "Wrong fail message: "+msg); - assertTrue(msg.contains("InvalidDefinitionException"), - "Wrong fail message: "+msg); + assertEquals(dt.toString(), msg); } } From c78b6cb03da5786b1c6f57ab795058cb2edd9e29 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 17:02:54 +0100 Subject: [PATCH 09/21] Update POJONodeTest.java --- src/test/java/tools/jackson/databind/node/POJONodeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/tools/jackson/databind/node/POJONodeTest.java b/src/test/java/tools/jackson/databind/node/POJONodeTest.java index ee7176cd1c..6fe01159e6 100644 --- a/src/test/java/tools/jackson/databind/node/POJONodeTest.java +++ b/src/test/java/tools/jackson/databind/node/POJONodeTest.java @@ -65,13 +65,13 @@ public void testPOJONodeCustomSer() throws Exception @Test public void testAddJava8DateAsPojo() throws Exception { - LocalDateTime dt = LocalDateTime.now(); + LocalDateTime dt = LocalDateTime.parse("2025-03-31T12:00"); JsonNode node = MAPPER.createObjectNode().putPOJO("test", dt); String json = node.toString(); assertNotNull(json); JsonNode result = MAPPER.readTree(json); String msg = result.path("test").asString(); - assertEquals(dt.toString(), msg); + assertEquals(dt, LocalDateTime.parse(msg)); } } From 67e415477f720b502d32a101510315bb56506ac9 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 17:15:04 +0100 Subject: [PATCH 10/21] make method private - class is final anyway --- .../tools/jackson/databind/datetime/JavaTimeInitializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java index 749fc23671..757c78afa7 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java @@ -205,7 +205,7 @@ public ValueInstantiator modifyValueInstantiator(DeserializationConfig config, }); } - protected AnnotatedMethod _findFactory(AnnotatedClass cls, String name, Class... argTypes) + private AnnotatedMethod _findFactory(AnnotatedClass cls, String name, Class... argTypes) { final int argCount = argTypes.length; for (AnnotatedMethod method : cls.getFactoryMethods()) { From 30b97d8ab9de02225b8d0432fa853175615d4964 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 18 Mar 2025 19:17:35 +0100 Subject: [PATCH 11/21] Update module-info.java --- src/main/java/module-info.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 227484b7be..a4567d076a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -27,8 +27,9 @@ exports tools.jackson.databind.cfg; exports tools.jackson.databind.datetime; exports tools.jackson.databind.datetime.deser; + exports tools.jackson.databind.datetime.deser.key; exports tools.jackson.databind.datetime.ser; - exports tools.jackson.databind.datetime.util; + exports tools.jackson.databind.datetime.ser.key; exports tools.jackson.databind.deser; exports tools.jackson.databind.deser.bean; // Alas multiple types from this package are exported. Would From 13938217388a75acd770fc411da17c1ab8cfadec Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 19 Mar 2025 10:54:48 +0100 Subject: [PATCH 12/21] change package name --- src/main/java/module-info.java | 10 +++++----- .../tools/jackson/databind/JacksonModule.java | 2 +- .../jackson/databind/cfg/MapperBuilder.java | 4 ++-- .../jackson/databind/cfg/ModuleContextBase.java | 2 +- .../{ => ext}/datetime/JavaTimeFeature.java | 2 +- .../{ => ext}/datetime/JavaTimeInitializer.java | 10 +++++----- .../datetime/deser/DurationDeserializer.java | 6 +++--- .../datetime/deser/InstantDeserializer.java | 10 +++++----- .../deser/JSR310DateTimeDeserializerBase.java | 2 +- .../datetime/deser/JSR310DeserializerBase.java | 2 +- .../deser/JSR310StringParsableDeserializer.java | 2 +- .../deser/JavaTimeDeserializerModifier.java | 2 +- .../datetime/deser/LocalDateDeserializer.java | 6 +++--- .../deser/LocalDateTimeDeserializer.java | 6 +++--- .../datetime/deser/LocalTimeDeserializer.java | 2 +- .../datetime/deser/MonthDayDeserializer.java | 2 +- .../datetime/deser/OffsetTimeDeserializer.java | 2 +- .../deser/OneBasedMonthDeserializer.java | 2 +- .../datetime/deser/YearDeserializer.java | 2 +- .../datetime/deser/YearMonthDeserializer.java | 2 +- .../deser/key/DurationKeyDeserializer.java | 2 +- .../deser/key/InstantKeyDeserializer.java | 2 +- .../deser/key/Jsr310KeyDeserializer.java | 2 +- .../deser/key/LocalDateKeyDeserializer.java | 2 +- .../deser/key/LocalDateTimeKeyDeserializer.java | 2 +- .../deser/key/LocalTimeKeyDeserializer.java | 2 +- .../deser/key/MonthDayKeyDeserializer.java | 2 +- .../deser/key/OffsetDateTimeKeyDeserializer.java | 2 +- .../deser/key/OffsetTimeKeyDeserializer.java | 2 +- .../deser/key/PeriodKeyDeserializer.java | 2 +- .../datetime/deser/key/YearKeyDeserializer.java | 2 +- .../deser/key/YearMonthKeyDeserializer.java | 2 +- .../deser/key/ZoneIdKeyDeserializer.java | 2 +- .../deser/key/ZoneOffsetKeyDeserializer.java | 2 +- .../deser/key/ZonedDateTimeKeyDeserializer.java | 2 +- .../datetime/ser/DurationSerializer.java | 6 +++--- .../datetime/ser/InstantSerializer.java | 2 +- .../datetime/ser/InstantSerializerBase.java | 4 ++-- .../ser/JSR310FormattedSerializerBase.java | 2 +- .../datetime/ser/JSR310SerializerBase.java | 2 +- .../datetime/ser/JavaTimeSerializerModifier.java | 2 +- .../datetime/ser/LocalDateSerializer.java | 2 +- .../datetime/ser/LocalDateTimeSerializer.java | 2 +- .../datetime/ser/LocalTimeSerializer.java | 2 +- .../datetime/ser/MonthDaySerializer.java | 2 +- .../datetime/ser/OffsetDateTimeSerializer.java | 2 +- .../datetime/ser/OffsetTimeSerializer.java | 2 +- .../datetime/ser/OneBasedMonthSerializer.java | 2 +- .../datetime/ser/YearMonthSerializer.java | 2 +- .../{ => ext}/datetime/ser/YearSerializer.java | 2 +- .../{ => ext}/datetime/ser/ZoneIdSerializer.java | 2 +- .../datetime/ser/ZonedDateTimeSerializer.java | 2 +- .../ser/key/ZonedDateTimeKeySerializer.java | 4 ++-- .../{ => ext}/datetime/util/DecimalUtils.java | 2 +- .../datetime/util/DurationUnitConverter.java | 4 ++-- .../tools/jackson/databind/json/JsonMapper.java | 2 +- src/test/java/module-info.java | 16 ++++++++-------- .../datetime/MockObjectConfiguration.java | 2 +- .../{ => ext}/datetime/ModuleTestBase.java | 2 +- .../{ => ext}/datetime/TestDecimalUtils.java | 4 ++-- .../{ => ext}/datetime/TestFeatures.java | 2 +- .../datetime/deser/DefaultTypingTest.java | 4 ++-- .../datetime/deser/DurationDeser337Test.java | 4 ++-- .../datetime/deser/DurationDeserTest.java | 6 +++--- .../datetime/deser/InstantDeser291Test.java | 6 +++--- .../datetime/deser/InstantDeserTest.java | 10 +++++----- .../datetime/deser/LocalDateDeserTest.java | 8 ++++---- .../datetime/deser/LocalDateTimeDeserTest.java | 8 ++++---- .../datetime/deser/LocalTimeDeserTest.java | 6 +++--- .../datetime/deser/MonthDayDeserTest.java | 6 +++--- .../datetime/deser/OffsetDateTimeDeserTest.java | 8 ++++---- .../datetime/deser/OffsetTimeDeserTest.java | 7 +++---- .../datetime/deser/OneBasedMonthDeserTest.java | 8 ++++---- .../datetime/deser/PeriodDeserTest.java | 6 +++--- .../{ => ext}/datetime/deser/YearDeserTest.java | 6 +++--- .../datetime/deser/YearMonthDeserTest.java | 4 ++-- .../datetime/deser/ZoneIdDeserTest.java | 6 +++--- .../datetime/deser/ZoneOffsetDeserTest.java | 6 +++--- .../datetime/deser/ZonedDateTimeDeserTest.java | 6 +++--- .../key/ZonedDateTimeKeyDeserializerTest.java | 4 ++-- .../datetime/key/DurationAsKeyTest.java | 4 ++-- .../{ => ext}/datetime/key/InstantAsKeyTest.java | 4 ++-- .../datetime/key/LocalDateAsKeyTest.java | 4 ++-- .../datetime/key/LocalDateTimeAsKeyTest.java | 4 ++-- .../datetime/key/LocalTimeAsKeyTest.java | 4 ++-- .../datetime/key/MonthDayAsKeyTest.java | 4 ++-- .../datetime/key/OffsetDateTimeAsKeyTest.java | 4 ++-- .../datetime/key/OffsetTimeAsKeyTest.java | 4 ++-- .../{ => ext}/datetime/key/PeriodAsKeyTest.java | 4 ++-- .../{ => ext}/datetime/key/YearAsKeyTest.java | 4 ++-- .../datetime/key/YearMonthAsKeyTest.java | 4 ++-- .../{ => ext}/datetime/key/ZoneIdAsKeyTest.java | 4 ++-- .../datetime/key/ZoneOffsetAsKeyTest.java | 4 ++-- .../datetime/key/ZonedDateTimeAsKeyTest.java | 4 ++-- .../datetime/misc/DateTimeExceptionTest.java | 4 ++-- .../datetime/misc/DateTimeSchemasTest.java | 4 ++-- .../misc/DeductionTypeSerialization296Test.java | 4 ++-- .../datetime/misc/JDKSerializabilityTest.java | 4 ++-- .../datetime/misc/UnsupportedTypesTest.java | 4 ++-- .../{ => ext}/datetime/ser/DurationSerTest.java | 6 +++--- .../{ => ext}/datetime/ser/InstantSerTest.java | 8 ++++---- .../{ => ext}/datetime/ser/LocalDateSerTest.java | 6 +++--- .../datetime/ser/LocalDateTimeSerTest.java | 6 +++--- .../{ => ext}/datetime/ser/LocalTimeSerTest.java | 6 +++--- .../{ => ext}/datetime/ser/MonthDaySerTest.java | 6 +++--- .../datetime/ser/OffsetDateTimeSerTest.java | 8 ++++---- .../datetime/ser/OffsetTimeSerTest.java | 6 +++--- .../datetime/ser/OneBasedMonthSerTest.java | 6 +++--- .../{ => ext}/datetime/ser/PeriodSerTest.java | 6 +++--- ...ocalDateSerializationWithCustomFormatter.java | 5 +++-- ...DateTimeSerializationWithCustomFormatter.java | 5 +++-- ...ocalTimeSerializationWithCustomFormatter.java | 5 +++-- ...earMonthSerializationWithCustomFormatter.java | 5 +++-- ...TestYearSerializationWithCustomFormatter.java | 5 +++-- ...DateTimeSerializationWithCustomFormatter.java | 3 ++- .../datetime/ser/WriteNanosecondsTest.java | 4 ++-- .../{ => ext}/datetime/ser/WriteZoneIdTest.java | 6 +++--- .../datetime/ser/YearMonthSerializationTest.java | 6 +++--- .../{ => ext}/datetime/ser/YearSerTest.java | 6 +++--- .../{ => ext}/datetime/ser/ZoneIdSerTest.java | 6 +++--- .../datetime/ser/ZoneOffsetSerTest.java | 6 +++--- .../datetime/ser/ZonedDateTimeSerTest.java | 10 +++++----- .../ZonedDateTimeSerWithJsonFormat333Test.java | 4 ++-- .../InstantDeserializerNegative359Test.java | 4 ++-- .../tofix/InstantViaBigDecimal307Test.java | 4 ++-- .../tofix/OffsetDateTimeDeser279Test.java | 4 ++-- .../tofix/ZonedDateTimeIssue244Test.java | 4 ++-- .../datetime/util/DurationUnitConverterTest.java | 4 ++-- 128 files changed, 273 insertions(+), 268 deletions(-) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/JavaTimeFeature.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/JavaTimeInitializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/DurationDeserializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/InstantDeserializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/JSR310DateTimeDeserializerBase.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/JSR310DeserializerBase.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/JSR310StringParsableDeserializer.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/JavaTimeDeserializerModifier.java (94%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalDateDeserializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalDateTimeDeserializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalTimeDeserializer.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/MonthDayDeserializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/OffsetTimeDeserializer.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/OneBasedMonthDeserializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/YearDeserializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/YearMonthDeserializer.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/DurationKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/InstantKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/Jsr310KeyDeserializer.java (96%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/LocalDateKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/LocalDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/LocalTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/MonthDayKeyDeserializer.java (95%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/OffsetDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/OffsetTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/PeriodKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/YearKeyDeserializer.java (94%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/YearMonthKeyDeserializer.java (95%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/ZoneIdKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/ZoneOffsetKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/deser/key/ZonedDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/DurationSerializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/InstantSerializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/InstantSerializerBase.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/JSR310FormattedSerializerBase.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/JSR310SerializerBase.java (96%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/JavaTimeSerializerModifier.java (94%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalDateSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalDateTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalTimeSerializer.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/MonthDaySerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/OffsetDateTimeSerializer.java (97%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/OffsetTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/OneBasedMonthSerializer.java (95%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/YearMonthSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/YearSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/ZoneIdSerializer.java (95%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/ZonedDateTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/ser/key/ZonedDateTimeKeySerializer.java (93%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/util/DecimalUtils.java (99%) rename src/main/java/tools/jackson/databind/{ => ext}/datetime/util/DurationUnitConverter.java (95%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/MockObjectConfiguration.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ModuleTestBase.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/TestDecimalUtils.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/TestFeatures.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/DefaultTypingTest.java (93%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/DurationDeser337Test.java (92%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/DurationDeserTest.java (99%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/InstantDeser291Test.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/InstantDeserTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalDateDeserTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalDateTimeDeserTest.java (99%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/LocalTimeDeserTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/MonthDayDeserTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/OffsetDateTimeDeserTest.java (99%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/OffsetTimeDeserTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/OneBasedMonthDeserTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/PeriodDeserTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/YearDeserTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/YearMonthDeserTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/ZoneIdDeserTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/ZoneOffsetDeserTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/ZonedDateTimeDeserTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java (95%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/DurationAsKeyTest.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/InstantAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/LocalDateAsKeyTest.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/LocalDateTimeAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/LocalTimeAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/MonthDayAsKeyTest.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/OffsetDateTimeAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/OffsetTimeAsKeyTest.java (95%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/PeriodAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/YearAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/YearMonthAsKeyTest.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/ZoneIdAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/ZoneOffsetAsKeyTest.java (93%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/key/ZonedDateTimeAsKeyTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/misc/DateTimeExceptionTest.java (85%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/misc/DateTimeSchemasTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/misc/DeductionTypeSerialization296Test.java (95%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/misc/JDKSerializabilityTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/misc/UnsupportedTypesTest.java (88%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/DurationSerTest.java (98%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/InstantSerTest.java (96%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalDateSerTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalDateTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/LocalTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/MonthDaySerTest.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/OffsetDateTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/OffsetTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/OneBasedMonthSerTest.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/PeriodSerTest.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java (92%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestYearSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java (93%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/WriteNanosecondsTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/WriteZoneIdTest.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/YearMonthSerializationTest.java (97%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/YearSerTest.java (93%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/ZoneIdSerTest.java (90%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/ZoneOffsetSerTest.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/ZonedDateTimeSerTest.java (99%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java (92%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/tofix/InstantDeserializerNegative359Test.java (91%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/tofix/InstantViaBigDecimal307Test.java (93%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/tofix/OffsetDateTimeDeser279Test.java (92%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/tofix/ZonedDateTimeIssue244Test.java (94%) rename src/test/java/tools/jackson/databind/{ => ext}/datetime/util/DurationUnitConverterTest.java (93%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index a4567d076a..14a3b0e20f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -25,11 +25,6 @@ exports tools.jackson.databind; exports tools.jackson.databind.annotation; exports tools.jackson.databind.cfg; - exports tools.jackson.databind.datetime; - exports tools.jackson.databind.datetime.deser; - exports tools.jackson.databind.datetime.deser.key; - exports tools.jackson.databind.datetime.ser; - exports tools.jackson.databind.datetime.ser.key; exports tools.jackson.databind.deser; exports tools.jackson.databind.deser.bean; // Alas multiple types from this package are exported. Would @@ -40,6 +35,11 @@ exports tools.jackson.databind.deser.std; exports tools.jackson.databind.exc; exports tools.jackson.databind.ext; + exports tools.jackson.databind.ext.datetime; + exports tools.jackson.databind.ext.datetime.deser; + exports tools.jackson.databind.ext.datetime.deser.key; + exports tools.jackson.databind.ext.datetime.ser; + exports tools.jackson.databind.ext.datetime.ser.key; exports tools.jackson.databind.ext.jdk8; // Needed by Ion module for SqlDate deserializer: exports tools.jackson.databind.ext.sql; diff --git a/src/main/java/tools/jackson/databind/JacksonModule.java b/src/main/java/tools/jackson/databind/JacksonModule.java index c9eec09f6f..8324c0da76 100644 --- a/src/main/java/tools/jackson/databind/JacksonModule.java +++ b/src/main/java/tools/jackson/databind/JacksonModule.java @@ -8,7 +8,7 @@ import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.cfg.MapperBuilder; import tools.jackson.databind.cfg.MutableConfigOverride; -import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.Serializers; diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index a8dd49040f..2dd51a5775 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -16,8 +16,8 @@ import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; -import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.JavaTimeInitializer; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.ext.datetime.JavaTimeInitializer; import tools.jackson.databind.deser.*; import tools.jackson.databind.introspect.*; import tools.jackson.databind.jsontype.*; diff --git a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java index 73242fcb4d..9d6211d7b9 100644 --- a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java +++ b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java @@ -7,7 +7,7 @@ import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.*; import tools.jackson.databind.JacksonModule.SetupContext; -import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.SerializerFactory; diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java similarity index 98% rename from src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java rename to src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java index 1c6393aa8a..2fcbf418a8 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.datetime; +package tools.jackson.databind.ext.datetime; import tools.jackson.core.util.JacksonFeature; diff --git a/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java rename to src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java index 757c78afa7..ccfd902918 100644 --- a/src/main/java/tools/jackson/databind/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime; +package tools.jackson.databind.ext.datetime; import java.time.*; @@ -22,6 +22,9 @@ import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; import tools.jackson.databind.deser.std.StdValueInstantiator; +import tools.jackson.databind.ext.datetime.deser.*; +import tools.jackson.databind.ext.datetime.deser.key.*; +import tools.jackson.databind.ext.datetime.ser.*; import tools.jackson.databind.introspect.AnnotatedClass; import tools.jackson.databind.introspect.AnnotatedClassResolver; import tools.jackson.databind.introspect.AnnotatedMethod; @@ -30,10 +33,7 @@ import tools.jackson.databind.module.SimpleSerializers; import tools.jackson.databind.ser.std.ToStringSerializer; -import tools.jackson.databind.datetime.deser.*; -import tools.jackson.databind.datetime.deser.key.*; -import tools.jackson.databind.datetime.ser.*; -import tools.jackson.databind.datetime.ser.key.ZonedDateTimeKeySerializer; +import tools.jackson.databind.ext.datetime.ser.key.ZonedDateTimeKeySerializer; /** * Class that registers capability of serializing {@code java.time} objects with the Jackson core. diff --git a/src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java index 3b27463d3c..c2e39f9cbc 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/DurationDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.math.BigDecimal; import java.time.DateTimeException; @@ -29,8 +29,8 @@ import tools.jackson.core.StreamReadCapability; import tools.jackson.core.io.NumberInput; import tools.jackson.databind.*; -import tools.jackson.databind.datetime.util.DecimalUtils; -import tools.jackson.databind.datetime.util.DurationUnitConverter; +import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.datetime.util.DurationUnitConverter; /** * Deserializer for Java 8 temporal {@link Duration}s. diff --git a/src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java index bd5659cc29..4844e92679 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/InstantDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.math.BigDecimal; import java.time.*; @@ -35,8 +35,8 @@ import tools.jackson.databind.BeanProperty; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.DeserializationFeature; -import tools.jackson.databind.datetime.JavaTimeFeature; -import tools.jackson.databind.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.ext.datetime.util.DecimalUtils; /** * Deserializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime}, @@ -133,7 +133,7 @@ private static OffsetDateTime decimalToOffsetDateTime(FromDecimalArguments args) /** * Flag set from - * {@link tools.jackson.databind.datetime.JavaTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to + * {@link JavaTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to * determine whether {@link ZoneId} is to be normalized during deserialization. * * @since 2.16 @@ -142,7 +142,7 @@ private static OffsetDateTime decimalToOffsetDateTime(FromDecimalArguments args) /** * Flag set from - * {@link tools.jackson.databind.datetime.JavaTimeFeature#ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS} + * {@link JavaTimeFeature#ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS} * to determine whether stringified numbers are interpreted as timestamps * (enabled) nor not (disabled) in addition to a custom pattern ({code DateTimeFormatter}). *

diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java similarity index 99% rename from src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java index e87ca927f1..668ea8ef6c 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DateTimeDeserializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java similarity index 99% rename from src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java index 93b50c9048..67b534d218 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/JSR310DeserializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.time.DateTimeException; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java index 93161fac17..f77a7fabd2 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/JSR310StringParsableDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.time.DateTimeException; import java.time.Period; diff --git a/src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java similarity index 94% rename from src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java index f36d7c1802..6a3fb92526 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/JavaTimeDeserializerModifier.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.time.Month; diff --git a/src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java rename to src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java index 6e8851e586..cab6112586 100644 --- a/src/main/java/tools/jackson/databind/datetime/deser/LocalDateDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.datetime.deser; +package tools.jackson.databind.ext.datetime.deser; import java.time.DateTimeException; import java.time.Instant; @@ -32,7 +32,7 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; -import tools.jackson.databind.datetime.JavaTimeFeature; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** * Deserializer for Java 8 temporal {@link LocalDate}s. @@ -50,7 +50,7 @@ public class LocalDateDeserializer extends JSR310DateTimeDeserializerBase Date: Thu, 3 Apr 2025 20:46:58 -0700 Subject: [PATCH 13/21] Merge https://github.com/FasterXML/jackson-modules-java8/issues/364 changes --- .../deser/OneBasedMonthDeserializer.java | 57 ++++++++++++----- ...uleTestBase.java => DateTimeTestBase.java} | 2 +- .../ext/datetime/MockObjectConfiguration.java | 16 ----- .../ext/datetime/TestDecimalUtils.java | 2 +- .../ext/datetime/deser/DefaultTypingTest.java | 4 +- .../datetime/deser/DurationDeser337Test.java | 4 +- .../ext/datetime/deser/DurationDeserTest.java | 4 +- .../datetime/deser/InstantDeser291Test.java | 4 +- .../ext/datetime/deser/InstantDeserTest.java | 4 +- .../datetime/deser/LocalDateDeserTest.java | 4 +- .../deser/LocalDateTimeDeserTest.java | 4 +- .../datetime/deser/LocalTimeDeserTest.java | 4 +- .../ext/datetime/deser/MonthDayDeserTest.java | 4 +- .../deser/OffsetDateTimeDeserTest.java | 4 +- .../datetime/deser/OffsetTimeDeserTest.java | 4 +- .../deser/OneBasedMonthDeserTest.java | 62 ++++++++++++------- .../ext/datetime/deser/PeriodDeserTest.java | 4 +- .../ext/datetime/deser/YearDeserTest.java | 4 +- .../datetime/deser/YearMonthDeserTest.java | 4 +- .../ext/datetime/deser/ZoneIdDeserTest.java | 4 +- .../datetime/deser/ZoneOffsetDeserTest.java | 4 +- .../deser/ZonedDateTimeDeserTest.java | 4 +- .../key/ZonedDateTimeKeyDeserializerTest.java | 4 +- .../ext/datetime/key/DurationAsKeyTest.java | 4 +- .../ext/datetime/key/InstantAsKeyTest.java | 4 +- .../ext/datetime/key/LocalDateAsKeyTest.java | 4 +- .../datetime/key/LocalDateTimeAsKeyTest.java | 4 +- .../ext/datetime/key/LocalTimeAsKeyTest.java | 4 +- .../ext/datetime/key/MonthDayAsKeyTest.java | 4 +- .../datetime/key/OffsetDateTimeAsKeyTest.java | 4 +- .../ext/datetime/key/OffsetTimeAsKeyTest.java | 4 +- .../ext/datetime/key/PeriodAsKeyTest.java | 4 +- .../ext/datetime/key/YearAsKeyTest.java | 4 +- .../ext/datetime/key/YearMonthAsKeyTest.java | 4 +- .../ext/datetime/key/ZoneIdAsKeyTest.java | 4 +- .../ext/datetime/key/ZoneOffsetAsKeyTest.java | 4 +- .../datetime/key/ZonedDateTimeAsKeyTest.java | 4 +- .../datetime/misc/DateTimeExceptionTest.java | 4 +- .../datetime/misc/DateTimeSchemasTest.java | 4 +- .../DeductionTypeSerialization296Test.java | 4 +- .../datetime/misc/JDKSerializabilityTest.java | 2 +- .../datetime/misc/UnsupportedTypesTest.java | 4 +- .../ext/datetime/ser/DurationSerTest.java | 4 +- .../ext/datetime/ser/InstantSerTest.java | 4 +- .../ext/datetime/ser/LocalDateSerTest.java | 4 +- .../datetime/ser/LocalDateTimeSerTest.java | 4 +- .../ext/datetime/ser/LocalTimeSerTest.java | 4 +- .../ext/datetime/ser/MonthDaySerTest.java | 4 +- .../datetime/ser/OffsetDateTimeSerTest.java | 4 +- .../ext/datetime/ser/OffsetTimeSerTest.java | 4 +- .../datetime/ser/OneBasedMonthSerTest.java | 4 +- .../ext/datetime/ser/PeriodSerTest.java | 4 +- .../datetime/ser/WriteNanosecondsTest.java | 4 +- .../ext/datetime/ser/WriteZoneIdTest.java | 4 +- .../ser/YearMonthSerializationTest.java | 4 +- .../ext/datetime/ser/YearSerTest.java | 4 +- .../ext/datetime/ser/ZoneIdSerTest.java | 4 +- .../ext/datetime/ser/ZoneOffsetSerTest.java | 4 +- .../datetime/ser/ZonedDateTimeSerTest.java | 4 +- ...ZonedDateTimeSerWithJsonFormat333Test.java | 4 +- .../InstantDeserializerNegative359Test.java | 4 +- .../tofix/InstantViaBigDecimal307Test.java | 4 +- .../tofix/OffsetDateTimeDeser279Test.java | 4 +- .../tofix/ZonedDateTimeIssue244Test.java | 4 +- .../util/DurationUnitConverterTest.java | 4 +- 65 files changed, 200 insertions(+), 177 deletions(-) rename src/test/java/tools/jackson/databind/ext/datetime/{ModuleTestBase.java => DateTimeTestBase.java} (98%) diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java index 0cef6c6762..f0b21a6318 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java @@ -1,7 +1,6 @@ package tools.jackson.databind.ext.datetime.deser; import java.time.Month; -import java.util.regex.Pattern; import tools.jackson.core.JsonParser; import tools.jackson.core.JsonToken; @@ -10,12 +9,7 @@ import tools.jackson.databind.deser.std.DelegatingDeserializer; import tools.jackson.databind.exc.InvalidFormatException; -/** - * @since 2.17 - */ public class OneBasedMonthDeserializer extends DelegatingDeserializer { - private static final Pattern HAS_ONE_OR_TWO_DIGITS = Pattern.compile("^\\d{1,2}$"); - public OneBasedMonthDeserializer(ValueDeserializer defaultDeserializer) { super(defaultDeserializer); } @@ -23,22 +17,51 @@ public OneBasedMonthDeserializer(ValueDeserializer defaultDeserializer) { @Override public Object deserialize(JsonParser parser, DeserializationContext context) { JsonToken token = parser.currentToken(); - Month zeroBaseMonth = (Month) getDelegatee().deserialize(parser, context); - if (!_isNumericValue(parser.getString(), token)) { - return zeroBaseMonth; - } - if (zeroBaseMonth == Month.JANUARY) { - throw new InvalidFormatException(parser, "Month.JANUARY value not allowed for 1-based Month.", zeroBaseMonth, Month.class); + switch (token) { + case VALUE_NUMBER_INT: + return _decodeMonth(parser.getIntValue(), parser); + case VALUE_STRING: + String monthSpec = parser.getString(); + int oneBasedMonthNumber = _decodeNumber(monthSpec); + if (oneBasedMonthNumber >= 0) { + return _decodeMonth(oneBasedMonthNumber, parser); + } + default: + // Otherwise fall through to default handling + break; } - return zeroBaseMonth.minus(1); + return getDelegatee().deserialize(parser, context); } - private boolean _isNumericValue(String text, JsonToken token) { - return token == JsonToken.VALUE_NUMBER_INT || _isNumberAsString(text, token); + /** + * @return Numeric value of input text that represents a 1-digit or 2-digit number. + * Negative value in other cases (empty string, not a number, 3 or more digits). + */ + private int _decodeNumber(String text) { + int numValue; + switch (text.length()) { + case 1: + char c = text.charAt(0); + boolean cValid = ('0' <= c && c <= '9'); + numValue = cValid ? (c - '0') : -1; + break; + case 2: + char c1 = text.charAt(0); + char c2 = text.charAt(1); + boolean c12valid = ('0' <= c1 && c1 <= '9' && '0' <= c2 && c2 <= '9'); + numValue = c12valid ? (10 * (c1 - '0') + (c2 - '0')) : -1; + break; + default: + numValue = -1; + } + return numValue; } - private boolean _isNumberAsString(String text, JsonToken token) { - return token == JsonToken.VALUE_STRING && HAS_ONE_OR_TWO_DIGITS.matcher(text).matches(); + private Month _decodeMonth(int oneBasedMonthNumber, JsonParser parser) throws InvalidFormatException { + if (Month.JANUARY.getValue() <= oneBasedMonthNumber && oneBasedMonthNumber <= Month.DECEMBER.getValue()) { + return Month.of(oneBasedMonthNumber); + } + throw new InvalidFormatException(parser, "Month number " + oneBasedMonthNumber + " not allowed for 1-based Month.", oneBasedMonthNumber, Integer.class); } @Override diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ModuleTestBase.java b/src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/ModuleTestBase.java rename to src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java index 83b516757e..34a651e28a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ModuleTestBase.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java @@ -8,7 +8,7 @@ import tools.jackson.databind.cfg.MapperBuilder; import tools.jackson.databind.json.JsonMapper; -public class ModuleTestBase +public class DateTimeTestBase { protected static final ZoneId UTC = ZoneId.of("UTC"); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java b/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java index f39c8a1f1a..1ea765938b 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java @@ -1,19 +1,3 @@ -/* - * Copyright 2013 FasterXML.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain - * a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - package tools.jackson.databind.ext.datetime; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java b/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java index ba907546d7..b490345118 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class TestDecimalUtils extends ModuleTestBase +public class TestDecimalUtils extends DateTimeTestBase { @Test public void testToDecimal01() diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java index 8088adf20d..dedf2a45dd 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java @@ -8,11 +8,11 @@ import tools.jackson.databind.JavaType; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.jsontype.PolymorphicTypeValidator; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DefaultTypingTest extends ModuleTestBase +public class DefaultTypingTest extends DateTimeTestBase { static class NoCheckSubTypeValidator extends PolymorphicTypeValidator.Base diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java index ad389512ae..09c8e39354 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java @@ -6,11 +6,11 @@ import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class DurationDeser337Test extends ModuleTestBase +public class DurationDeser337Test extends DateTimeTestBase { @Test public void testWithDurationsAsTimestamps() throws Exception diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java index 1f0ff133e5..9b290eeebe 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java @@ -21,11 +21,11 @@ import tools.jackson.databind.exc.InvalidDefinitionException; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DurationDeserTest extends ModuleTestBase +public class DurationDeserTest extends DateTimeTestBase { private final ObjectReader READER = newMapper().readerFor(Duration.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java index 136d302296..43ad05cee5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java @@ -8,14 +8,14 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; // [modules-java8#291] InstantDeserializer fails to parse negative numeric timestamp strings for // pre-1970 values. public class InstantDeser291Test - extends ModuleTestBase + extends DateTimeTestBase { private final JsonMapper MAPPER = JsonMapper.builder() .defaultLocale(Locale.ENGLISH) diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java index a696bec186..a7c187b900 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java @@ -14,7 +14,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; import com.fasterxml.jackson.annotation.JsonFormat; @@ -27,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.*; import static tools.jackson.databind.ext.datetime.deser.InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX; -public class InstantDeserTest extends ModuleTestBase +public class InstantDeserTest extends DateTimeTestBase { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; private static final String CUSTOM_PATTERN = "yyyy-MM-dd HH:mm:ss"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java index de94995b12..f2579e8e10 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java @@ -24,11 +24,11 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalDateDeserTest extends ModuleTestBase +public class LocalDateDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(LocalDate.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java index fc01a66db3..0915222fd1 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java @@ -41,12 +41,12 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class LocalDateTimeDeserTest - extends ModuleTestBase + extends DateTimeTestBase { private final static ObjectMapper MAPPER = newMapper(); private final static ObjectReader READER = MAPPER.readerFor(LocalDateTime.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java index dbc002ea01..7fc9ce75e0 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java @@ -34,11 +34,11 @@ import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalTimeDeserTest extends ModuleTestBase +public class LocalTimeDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(LocalTime.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java index 35bb55c57a..62449cff2a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java @@ -16,11 +16,11 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class MonthDayDeserTest extends ModuleTestBase +public class MonthDayDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(MonthDay.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java index 3e77806b42..74f0102abf 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java @@ -21,13 +21,13 @@ import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; public class OffsetDateTimeDeserTest - extends ModuleTestBase + extends DateTimeTestBase { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java index 044b65a143..065c4bf2af 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java @@ -18,11 +18,11 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class OffsetTimeDeserTest extends ModuleTestBase +public class OffsetTimeDeserTest extends DateTimeTestBase { private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java index 67a9962ede..d6420dd7b6 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java @@ -5,6 +5,9 @@ import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; @@ -15,11 +18,11 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class OneBasedMonthDeserTest extends ModuleTestBase +public class OneBasedMonthDeserTest extends DateTimeTestBase { static class Wrapper { public Month value; @@ -28,39 +31,51 @@ static class Wrapper { public Wrapper() { } } - @Test - public void testDeserializationAsString01_oneBased() throws Exception + @ParameterizedTest + @EnumSource(Month.class) + public void testDeserializationAsString01_oneBased(Month expectedMonth) throws Exception { - assertEquals(Month.JANUARY, readerForOneBased().readValue("\"1\"")); + int monthNum = expectedMonth.getValue(); + assertEquals(expectedMonth, readerForOneBased().readValue("\"" + monthNum + '"')); } - @Test - public void testDeserializationAsString01_zeroBased() throws Exception + @ParameterizedTest + @EnumSource(Month.class) + public void testDeserializationAsString01_zeroBased(Month expectedMonth) throws Exception { - assertEquals(Month.FEBRUARY, readerForZeroBased().readValue("\"1\"")); + int monthNum = expectedMonth.ordinal(); + assertEquals(expectedMonth, readerForZeroBased().readValue("\"" + monthNum + '"')); } - @Test - public void testDeserializationAsString02_oneBased() throws Exception + @ParameterizedTest + @EnumSource(Month.class) + public void testDeserializationAsString02_oneBased(Month month) throws Exception { - assertEquals(Month.JANUARY, readerForOneBased().readValue("\"JANUARY\"")); + assertEquals(month, readerForOneBased().readValue("\"" + month.name() + '"')); } - @Test - public void testDeserializationAsString02_zeroBased() throws Exception + @ParameterizedTest + @EnumSource(Month.class) + public void testDeserializationAsString02_zeroBased(Month month) throws Exception { - assertEquals(Month.JANUARY, readerForZeroBased().readValue("\"JANUARY\"")); - } - - @Test - public void testBadDeserializationAsString01_oneBased() { + assertEquals(month, readerForOneBased().readValue("\"" + month.name() + '"')); + } + + @ParameterizedTest + @CsvSource({ + "notamonth , 'Cannot deserialize value of type `java.time.Month` from String \"notamonth\": not one of the values accepted for Enum class:'", + "JANUAR , 'Cannot deserialize value of type `java.time.Month` from String \"JANUAR\": not one of the values accepted for Enum class:'", + "march , 'Cannot deserialize value of type `java.time.Month` from String \"march\": not one of the values accepted for Enum class:'", + "0 , 'Month number 0 not allowed for 1-based Month.'", + "13 , 'Month number 13 not allowed for 1-based Month.'", + }) + public void testBadDeserializationAsString01_oneBased(String monthSpec, String expectedMessage) { + String value = "\"" + monthSpec + '"'; assertError( - () -> readerForOneBased().readValue("\"notamonth\""), + () -> readerForOneBased().readValue(value), InvalidFormatException.class, - // Order of enumerated values not stable, so don't check: - "Cannot deserialize value of type `java.time.Month` from String \"notamonth\":" - +" not one of the values accepted for Enum class: [" + expectedMessage ); } @@ -107,8 +122,8 @@ public void testDeserialization02_oneBased() throws Exception public void testDeserializationWithTypeInfo01_oneBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() - .enable(JavaTimeFeature.ONE_BASED_MONTHS) .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .enable(JavaTimeFeature.ONE_BASED_MONTHS) .build(); TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",11]", TemporalAccessor.class); @@ -120,6 +135,7 @@ public void testDeserializationWithTypeInfo01_zeroBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) + .disable(JavaTimeFeature.ONE_BASED_MONTHS) .build(); TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",\"11\"]", TemporalAccessor.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java index 6297e03fae..60edd3a90b 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java @@ -31,11 +31,11 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.type.LogicalType; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class PeriodDeserTest extends ModuleTestBase +public class PeriodDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java index 24ae24fee4..47bde297ba 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java @@ -30,11 +30,11 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class YearDeserTest extends ModuleTestBase +public class YearDeserTest extends DateTimeTestBase { private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java index 0a2c2fcfe0..2e415e4098 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java @@ -17,11 +17,11 @@ import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class YearMonthDeserTest extends ModuleTestBase +public class YearMonthDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(YearMonth.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java index 39e026c51d..4f06dfdbd1 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java @@ -15,11 +15,11 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.type.LogicalType; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class ZoneIdDeserTest extends ModuleTestBase +public class ZoneIdDeserTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final TypeReference> MAP_TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java index 77610b9295..dc5cfea85f 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java @@ -32,11 +32,11 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.type.LogicalType; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class ZoneOffsetDeserTest extends ModuleTestBase +public class ZoneOffsetDeserTest extends DateTimeTestBase { private final static ObjectMapper MAPPER = newMapper(); private final static ObjectReader READER = MAPPER.readerFor(ZoneOffset.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java index ce14c5d04b..a1ee0da4bb 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java @@ -19,11 +19,11 @@ import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class ZonedDateTimeDeserTest extends ModuleTestBase +public class ZonedDateTimeDeserTest extends DateTimeTestBase { private final ObjectReader READER = newMapper().readerFor(ZonedDateTime.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java index 259fafe69f..2ef98a7eb7 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java @@ -8,13 +8,13 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; // for [modules-java8#306] public class ZonedDateTimeKeyDeserializerTest - extends ModuleTestBase + extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final TypeReference> MAP_TYPE_REF diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java index 6fb29d0b33..dfa4e0e8af 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java @@ -9,11 +9,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DurationAsKeyTest extends ModuleTestBase +public class DurationAsKeyTest extends DateTimeTestBase { private static final Duration DURATION = Duration.ofMinutes(13).plusSeconds(37).plusNanos(120 * 1000 * 1000L); private static final String DURATION_STRING = "PT13M37.12S"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java index 89b3be38f7..34fcefec9d 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class InstantAsKeyTest extends ModuleTestBase +public class InstantAsKeyTest extends DateTimeTestBase { private static final Instant INSTANT_0 = Instant.ofEpochMilli(0); private static final String INSTANT_0_STRING = "1970-01-01T00:00:00Z"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java index b22fc0a8c8..fcaaac660a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalDateAsKeyTest extends ModuleTestBase +public class LocalDateAsKeyTest extends DateTimeTestBase { private static final LocalDate DATE = LocalDate.of(2015, 3, 14); private static final String DATE_STRING = "2015-03-14"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java index 2998578986..a11b29430a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java @@ -11,11 +11,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.deser.DeserializationProblemHandler; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalDateTimeAsKeyTest extends ModuleTestBase +public class LocalDateTimeAsKeyTest extends DateTimeTestBase { private static final LocalDateTime DATE_TIME_0 = LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC); /* diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java index 5cd6f32960..20b4145775 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalTimeAsKeyTest extends ModuleTestBase +public class LocalTimeAsKeyTest extends DateTimeTestBase { private static final LocalTime TIME_0 = LocalTime.ofSecondOfDay(0); /* diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java index 3d5cb2bc1c..260ef5eca3 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class MonthDayAsKeyTest extends ModuleTestBase +public class MonthDayAsKeyTest extends DateTimeTestBase { private static final MonthDay MONTH_DAY = MonthDay.of(3, 14); private static final String MONTH_DAY_STRING = "--03-14"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java index dbe77e6b0a..d7517428ed 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java @@ -10,11 +10,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class OffsetDateTimeAsKeyTest extends ModuleTestBase +public class OffsetDateTimeAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java index 90cf82a975..6e0fd2a4b0 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java @@ -9,11 +9,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class OffsetTimeAsKeyTest extends ModuleTestBase +public class OffsetTimeAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java index 942a015352..cf72e6d349 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class PeriodAsKeyTest extends ModuleTestBase +public class PeriodAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java index 0e8acfe7e3..f0d0416339 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java @@ -10,11 +10,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.InvalidFormatException; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class YearAsKeyTest extends ModuleTestBase +public class YearAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java index 5b7284460e..2d677f6d18 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class YearMonthAsKeyTest extends ModuleTestBase +public class YearMonthAsKeyTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(new TypeReference>() { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java index 82feaeb38d..54727f31c5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java @@ -8,11 +8,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ZoneIdAsKeyTest extends ModuleTestBase +public class ZoneIdAsKeyTest extends DateTimeTestBase { private static final ZoneId ZONE_0 = ZoneId.of("UTC"); private static final String ZONE_0_STRING = "UTC"; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java index a16658c0e7..e36f5c4b12 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java @@ -7,11 +7,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ZoneOffsetAsKeyTest extends ModuleTestBase +public class ZoneOffsetAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java index 68b8584a41..6664345314 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java @@ -9,11 +9,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class ZonedDateTimeAsKeyTest extends ModuleTestBase +public class ZonedDateTimeAsKeyTest extends DateTimeTestBase { private static final TypeReference> TYPE_REF = new TypeReference>() { }; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java b/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java index bca10e6180..9dd77654f0 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java @@ -5,11 +5,11 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DateTimeExceptionTest extends ModuleTestBase +public class DateTimeExceptionTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java b/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java index 1a9922dfdc..200e52d8fd 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java @@ -10,11 +10,11 @@ import tools.jackson.core.JsonParser; import tools.jackson.databind.*; import tools.jackson.databind.jsonFormatVisitors.*; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DateTimeSchemasTest extends ModuleTestBase +public class DateTimeSchemasTest extends DateTimeTestBase { static class VisitorWrapper implements JsonFormatVisitorWrapper { SerializationContext serializationContext; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java b/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java index 9ce702731a..082570c43d 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java @@ -9,12 +9,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; // for [modules-java8#296]: problem with `JsonTypeInfo.Id.DEDUCTION` -public class DeductionTypeSerialization296Test extends ModuleTestBase +public class DeductionTypeSerialization296Test extends DateTimeTestBase { static class Wrapper { @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java b/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java index 6e18e56885..caa67da23a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class JDKSerializabilityTest extends ModuleTestBase +public class JDKSerializabilityTest extends DateTimeTestBase { @Test public void testJDKSerializability() throws Exception { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java b/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java index c71531b279..51aef6bf64 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java @@ -6,11 +6,11 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class UnsupportedTypesTest extends ModuleTestBase +public class UnsupportedTypesTest extends DateTimeTestBase { // [modules-java8#207] static class TAWrapper { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java index d435097145..a87359fadc 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java @@ -12,11 +12,11 @@ import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class DurationSerTest extends ModuleTestBase +public class DurationSerTest extends DateTimeTestBase { private final ObjectWriter WRITER = newMapper().writer(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java index 99b194f7da..7fb85dec33 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java @@ -26,12 +26,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; -public class InstantSerTest extends ModuleTestBase +public class InstantSerTest extends DateTimeTestBase { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java index f758dbf243..0902bc8d5c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java @@ -28,12 +28,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class LocalDateSerTest - extends ModuleTestBase + extends DateTimeTestBase { final static class EpochDayWrapper { @JsonFormat(shape=JsonFormat.Shape.NUMBER_INT) diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java index 3be2275649..54829be2f5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java @@ -27,12 +27,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class LocalDateTimeSerTest - extends ModuleTestBase + extends DateTimeTestBase { static class LDTWrapper { @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss") diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java index 2d3de4a4a9..162824da18 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java @@ -27,11 +27,11 @@ import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.annotation.JsonSerialize; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class LocalTimeSerTest extends ModuleTestBase +public class LocalTimeSerTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); private final ObjectWriter writer = MAPPER.writer(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java index 7cb4a08758..8871548b32 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java @@ -24,12 +24,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class MonthDaySerTest - extends ModuleTestBase + extends DateTimeTestBase { private ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java index 9d9728e1ba..8482e306eb 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java @@ -12,13 +12,13 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.assertEquals; public class OffsetDateTimeSerTest - extends ModuleTestBase + extends DateTimeTestBase { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java index 91006685e6..daa5789300 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java @@ -24,11 +24,11 @@ import tools.jackson.databind.*; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class OffsetTimeSerTest extends ModuleTestBase +public class OffsetTimeSerTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java index 80aad94e8c..d8de2dd87d 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java @@ -9,11 +9,11 @@ import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.ext.datetime.JavaTimeFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class OneBasedMonthSerTest extends ModuleTestBase +public class OneBasedMonthSerTest extends DateTimeTestBase { static class Wrapper { public Month month; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java index f5c4da043d..10a7279ad1 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java @@ -23,11 +23,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class PeriodSerTest extends ModuleTestBase +public class PeriodSerTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java index 2a2bf79a2b..7b071df332 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java @@ -8,12 +8,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.assertj.core.api.Assertions.assertThat; -public class WriteNanosecondsTest extends ModuleTestBase +public class WriteNanosecondsTest extends DateTimeTestBase { public static final ZoneId UTC = ZoneId.of("UTC"); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java index 9a739c04b7..f12c8a14a2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java @@ -11,11 +11,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class WriteZoneIdTest extends ModuleTestBase +public class WriteZoneIdTest extends DateTimeTestBase { static class DummyClassWithDate { @JsonFormat(shape = JsonFormat.Shape.STRING, diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java index 3a608eba63..d83c45cf96 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java @@ -28,12 +28,12 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class YearMonthSerializationTest - extends ModuleTestBase + extends DateTimeTestBase { private static class SimpleAggregate { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java index 8f17f64fa3..6c82635b7c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java @@ -25,11 +25,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class YearSerTest extends ModuleTestBase +public class YearSerTest extends DateTimeTestBase { final static class YearAsStringWrapper { @JsonFormat(shape = JsonFormat.Shape.STRING) diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java index 0bdc35ae46..6c741f4b5f 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java @@ -22,11 +22,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; -public class ZoneIdSerTest extends ModuleTestBase +public class ZoneIdSerTest extends DateTimeTestBase { private ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java index 2d0f47ecc2..2ee26fea5b 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java @@ -23,11 +23,11 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; -public class ZoneOffsetSerTest extends ModuleTestBase +public class ZoneOffsetSerTest extends DateTimeTestBase { private final ObjectMapper MAPPER = newMapper(); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java index fe8ce0533c..14e8574a30 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java @@ -36,13 +36,13 @@ import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; public class ZonedDateTimeSerTest - extends ModuleTestBase + extends DateTimeTestBase { private static final DateTimeFormatter FORMATTER_WITHOUT_ZONEID = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java index d5f3800900..a3721a9e6e 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java @@ -8,14 +8,14 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; // [module-java8#333]: ZonedDateTime serialization with @JsonFormat pattern never uses // while WRITE_DATES_WITH_ZONE_ID enabled #333 public class ZonedDateTimeSerWithJsonFormat333Test - extends ModuleTestBase + extends DateTimeTestBase { public static class ContainerWithPattern333 { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss z") diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java b/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java index a4ce7ab9a6..9c55c544ac 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,7 +14,7 @@ // fractional timestamps incorrectly: -1.000000001 deserializes to 1969-12-31T23:59:59.000000001Z // instead of 1969-12-31T23:59:58.999999999Z public class InstantDeserializerNegative359Test - extends ModuleTestBase + extends DateTimeTestBase { private final ObjectReader READER = newMapper().readerFor(Instant.class); diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java b/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java index 37693f4aa6..cd63f9349c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java @@ -7,14 +7,14 @@ import tools.jackson.databind.JsonNode; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; // [modules-java8#307]: Loss of precision via JsonNode for BigDecimal-valued // things (like Instant) -public class InstantViaBigDecimal307Test extends ModuleTestBase +public class InstantViaBigDecimal307Test extends DateTimeTestBase { public static class Wrapper307 { public Instant value; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java b/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java index 40d4fa23cc..a37c240ba7 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java @@ -9,12 +9,12 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; -public class OffsetDateTimeDeser279Test extends ModuleTestBase +public class OffsetDateTimeDeser279Test extends DateTimeTestBase { // For [modules-java8#279] static class Wrapper279 { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java b/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java index 0c84c11d62..dfc5721461 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java @@ -8,7 +8,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; @@ -16,7 +16,7 @@ /** * Test case for https://github.com/FasterXML/jackson-modules-java8/issues/244 */ -public class ZonedDateTimeIssue244Test extends ModuleTestBase +public class ZonedDateTimeIssue244Test extends DateTimeTestBase { private final ObjectMapper MAPPER = mapperBuilder() .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) diff --git a/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java b/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java index fafa0e1d6a..7e254252ab 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java @@ -4,12 +4,12 @@ import org.junit.jupiter.api.Test; -import tools.jackson.databind.ext.datetime.ModuleTestBase; +import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; public class DurationUnitConverterTest - extends ModuleTestBase + extends DateTimeTestBase { @Test public void shouldMapToTemporalUnit() { From 6f4338935fa2c70b1213e0730f6a9fa757b235e7 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 3 Apr 2025 20:50:25 -0700 Subject: [PATCH 14/21] Minor Javadoc improvements --- .../jackson/databind/ext/datetime/JavaTimeFeature.java | 4 +--- .../databind/ext/datetime/JavaTimeInitializer.java | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java index 2fcbf418a8..e23bf3d27a 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java @@ -4,8 +4,6 @@ /** * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). - * - * @since 2.16 */ public enum JavaTimeFeature implements JacksonFeature { @@ -54,7 +52,7 @@ public enum JavaTimeFeature implements JacksonFeature ONE_BASED_MONTHS(false) ; - /** + /** * Whether feature is enabled or disabled by default. */ private final boolean _defaultState; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java index ccfd902918..ff3c716321 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java @@ -38,7 +38,8 @@ /** * Class that registers capability of serializing {@code java.time} objects with the Jackson core. *

- * In Jackson 3, the module is automatically registered. + * In Jackson 3, the module is embedded in databind and handlers are automatically + * registered: approach is similar to one used by full {@link JacksonModule}s. *

* Most {@code java.time} types are serialized as numbers (integers or decimals as appropriate) if the * {@link tools.jackson.databind.SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} feature is enabled @@ -69,11 +70,6 @@ *

  • {@link LocalDate}, {@link LocalTime}, {@link LocalDateTime}, and {@link OffsetTime}, which cannot portably be converted to timestamps * and are instead represented as arrays when WRITE_DATES_AS_TIMESTAMPS is enabled.
  • * - * - * @author Nick Williams - * @author Zoltan Kiss - * - * @since 2.6 */ public final class JavaTimeInitializer implements java.io.Serializable From d6d8d045687d2ed54751feaa78ad722da2e179b0 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 3 Apr 2025 21:04:22 -0700 Subject: [PATCH 15/21] Start conversion to make `JavaTimeFeature` be accessed via `DatatypeFeatures` --- .../databind/cfg/DatatypeFeatures.java | 80 ++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java index c8621160ea..83219412c9 100644 --- a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java +++ b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java @@ -2,6 +2,7 @@ import tools.jackson.core.util.JacksonFeature; import tools.jackson.core.util.VersionUtil; +import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** * Immutable value class that contains settings for multiple @@ -14,17 +15,21 @@ public class DatatypeFeatures protected final static int FEATURE_INDEX_ENUM = 0; protected final static int FEATURE_INDEX_JSON_NODE = 1; + protected final static int FEATURE_INDEX_DATETIME = 2; - private final int _enabledFor1, _enabledFor2; + private final int _enabledFor1, _enabledFor2, _enabledFor3; - private final int _explicitFor1, _explicitFor2; + private final int _explicitFor1, _explicitFor2, _explicitFor3; protected DatatypeFeatures(int enabledFor1, int explicitFor1, - int enabledFor2, int explicitFor2) { + int enabledFor2, int explicitFor2, + int enabledFor3, int explicitFor3) { _enabledFor1 = enabledFor1; _explicitFor1 = explicitFor1; _enabledFor2 = enabledFor2; _explicitFor2 = explicitFor2; + _enabledFor3 = enabledFor3; + _explicitFor3 = explicitFor3; } public static DatatypeFeatures defaultFeatures() { @@ -32,13 +37,16 @@ public static DatatypeFeatures defaultFeatures() { } private DatatypeFeatures _with(int enabledFor1, int explicitFor1, - int enabledFor2, int explicitFor2) { + int enabledFor2, int explicitFor2, + int enabledFor3, int explicitFor3) { if ((_enabledFor1 == enabledFor1) && (_explicitFor1 == explicitFor1) - && (_enabledFor2 == enabledFor2) && (_explicitFor2 == explicitFor2)) { + && (_enabledFor2 == enabledFor2) && (_explicitFor2 == explicitFor2) + && (_enabledFor3 == enabledFor3) && (_explicitFor3 == explicitFor3)) { return this; } return new DatatypeFeatures(enabledFor1, explicitFor1, - enabledFor2, explicitFor2); + enabledFor2, explicitFor2, + enabledFor3, explicitFor3); } /* @@ -61,10 +69,16 @@ public DatatypeFeatures with(DatatypeFeature f) { switch (f.featureIndex()) { case 0: return _with(_enabledFor1 | mask, _explicitFor1 | mask, - _enabledFor2, _explicitFor2); + _enabledFor2, _explicitFor2, + _enabledFor3, _explicitFor3); case 1: return _with(_enabledFor1, _explicitFor1, - _enabledFor2 | mask, _explicitFor2 | mask); + _enabledFor2 | mask, _explicitFor2 | mask, + _enabledFor3, _explicitFor3); + case 2: + return _with(_enabledFor1, _explicitFor1, + _enabledFor2, _explicitFor2, + _enabledFor3 | mask, _explicitFor3 | mask); default: VersionUtil.throwInternal(); return this; @@ -88,10 +102,16 @@ public DatatypeFeatures withFeatures(DatatypeFeature... features) { switch (features[0].featureIndex()) { case 0: return _with(_enabledFor1 | mask, _explicitFor1 | mask, - _enabledFor2, _explicitFor2); + _enabledFor2, _explicitFor2, + _enabledFor3, _explicitFor3); case 1: return _with(_enabledFor1, _explicitFor1, - _enabledFor2 | mask, _explicitFor2 | mask); + _enabledFor2 | mask, _explicitFor2 | mask, + _enabledFor3, _explicitFor3); + case 3: + return _with(_enabledFor1, _explicitFor1, + _enabledFor2, _explicitFor2, + _enabledFor3 | mask, _explicitFor3 | mask); default: VersionUtil.throwInternal(); return this; @@ -112,10 +132,16 @@ public DatatypeFeatures without(DatatypeFeature f) { switch (f.featureIndex()) { case 0: return _with(_enabledFor1 & ~mask, _explicitFor1 | mask, - _enabledFor2, _explicitFor2); + _enabledFor2, _explicitFor2, + _enabledFor3, _explicitFor3); case 1: return _with(_enabledFor1, _explicitFor1, - _enabledFor2 & ~mask, _explicitFor2 | mask); + _enabledFor2 & ~mask, _explicitFor2 | mask, + _enabledFor3, _explicitFor3); + case 2: + return _with(_enabledFor1, _explicitFor1, + _enabledFor2, _explicitFor2, + _enabledFor3 & ~mask, _explicitFor3 | mask); default: VersionUtil.throwInternal(); return this; @@ -139,10 +165,16 @@ public DatatypeFeatures withoutFeatures(DatatypeFeature... features) { switch (features[0].featureIndex()) { case 0: return _with(_enabledFor1 & ~mask, _explicitFor1 | mask, - _enabledFor2, _explicitFor2); + _enabledFor2, _explicitFor2, + _enabledFor3, _explicitFor3); case 1: return _with(_enabledFor1, _explicitFor1, - _enabledFor2 & ~mask, _explicitFor2 | mask); + _enabledFor2 & ~mask, _explicitFor2 | mask, + _enabledFor3, _explicitFor3); + case 2: + return _with(_enabledFor1, _explicitFor1, + _enabledFor2, _explicitFor2, + _enabledFor3 & ~mask, _explicitFor3 | mask); default: VersionUtil.throwInternal(); return this; @@ -179,6 +211,8 @@ public boolean isEnabled(DatatypeFeature f) { return f.enabledIn(_enabledFor1); case 1: return f.enabledIn(_enabledFor2); + case 2: + return f.enabledIn(_enabledFor3); default: VersionUtil.throwInternal(); return false; @@ -200,6 +234,8 @@ public boolean isExplicitlySet(DatatypeFeature f) { return f.enabledIn(_explicitFor1); case 1: return f.enabledIn(_explicitFor2); + case 2: + return f.enabledIn(_explicitFor3); default: VersionUtil.throwInternal(); return false; @@ -215,8 +251,6 @@ public boolean isExplicitlySet(DatatypeFeature f) { * @param f Feature to check * * @return Whether given feature has been explicitly enabled - * - * @since 2.15 */ public boolean isExplicitlyEnabled(DatatypeFeature f) { switch (f.featureIndex()) { @@ -224,6 +258,8 @@ public boolean isExplicitlyEnabled(DatatypeFeature f) { return f.enabledIn(_explicitFor1 & _enabledFor1); case 1: return f.enabledIn(_explicitFor2 & _enabledFor2); + case 2: + return f.enabledIn(_explicitFor3 & _enabledFor3); default: VersionUtil.throwInternal(); return false; @@ -239,8 +275,6 @@ public boolean isExplicitlyEnabled(DatatypeFeature f) { * @param f Feature to check * * @return Whether given feature has been explicitly disabled - * - * @since 2.15 */ public boolean isExplicitlyDisabled(DatatypeFeature f) { switch (f.featureIndex()) { @@ -248,6 +282,8 @@ public boolean isExplicitlyDisabled(DatatypeFeature f) { return f.enabledIn(_explicitFor1 & ~_enabledFor1); case 1: return f.enabledIn(_explicitFor2 & ~_enabledFor2); + case 2: + return f.enabledIn(_explicitFor3 & ~_enabledFor3); default: VersionUtil.throwInternal(); return false; @@ -276,6 +312,11 @@ public Boolean getExplicitState(DatatypeFeature f) { return f.enabledIn(_enabledFor2); } return null; + case 2: + if (f.enabledIn(_explicitFor3)) { + return f.enabledIn(_enabledFor3); + } + return null; default: VersionUtil.throwInternal(); return null; @@ -298,7 +339,8 @@ private static class DefaultHolder static { DEFAULT_FEATURES = new DatatypeFeatures( collectDefaults(EnumFeature.values()), 0, - collectDefaults(JsonNodeFeature.values()), 0 + collectDefaults(JsonNodeFeature.values()), 0, + collectDefaults(JavaTimeFeature.values()), 0 ); } From 0f8a8905c5a25b5aa70c19f3c055f7cd15d19cf7 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 15:29:05 -0700 Subject: [PATCH 16/21] Bit of refactoring wrt JavaTimeFeature --- .../tools/jackson/databind/JacksonModule.java | 9 +++----- .../databind/cfg/DatatypeFeatures.java | 6 +++-- .../jackson/databind/cfg/MapperBuilder.java | 17 -------------- .../databind/cfg/ModuleContextBase.java | 11 +++++----- .../ext/datetime/JavaTimeFeature.java | 22 ++++++++++++------- .../ext/datetime/JavaTimeInitializer.java | 12 +++++----- .../datetime/deser/InstantDeserializer.java | 10 +++------ .../datetime/deser/LocalDateDeserializer.java | 7 +++--- .../deser/LocalDateTimeDeserializer.java | 6 ++--- .../jackson/databind/json/JsonMapper.java | 16 +------------- ...lDateSerializationWithCustomFormatter.java | 1 - ...eTimeSerializationWithCustomFormatter.java | 1 - ...lTimeSerializationWithCustomFormatter.java | 1 - ...MonthSerializationWithCustomFormatter.java | 5 ++--- ...tYearSerializationWithCustomFormatter.java | 1 - ...eTimeSerializationWithCustomFormatter.java | 5 ++--- 16 files changed, 47 insertions(+), 83 deletions(-) diff --git a/src/main/java/tools/jackson/databind/JacksonModule.java b/src/main/java/tools/jackson/databind/JacksonModule.java index 8324c0da76..1ef63632d7 100644 --- a/src/main/java/tools/jackson/databind/JacksonModule.java +++ b/src/main/java/tools/jackson/databind/JacksonModule.java @@ -5,10 +5,7 @@ import java.util.function.UnaryOperator; import tools.jackson.core.*; -import tools.jackson.core.util.JacksonFeatureSet; -import tools.jackson.databind.cfg.MapperBuilder; -import tools.jackson.databind.cfg.MutableConfigOverride; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.cfg.*; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.Serializers; @@ -148,9 +145,9 @@ public static interface SetupContext public boolean isEnabled(TokenStreamFactory.Feature f); public boolean isEnabled(StreamReadFeature f); public boolean isEnabled(StreamWriteFeature f); - public boolean isEnabled(JavaTimeFeature f); + public boolean isEnabled(DatatypeFeature f); - public JacksonFeatureSet getJavaTimeFeatures(); + public DatatypeFeatures datatypeFeatures(); /* /****************************************************************** diff --git a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java index 83219412c9..2eadb97147 100644 --- a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java +++ b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java @@ -15,7 +15,9 @@ public class DatatypeFeatures protected final static int FEATURE_INDEX_ENUM = 0; protected final static int FEATURE_INDEX_JSON_NODE = 1; - protected final static int FEATURE_INDEX_DATETIME = 2; + + // !!! TODO: make protected + public final static int FEATURE_INDEX_DATETIME = 2; private final int _enabledFor1, _enabledFor2, _enabledFor3; @@ -108,7 +110,7 @@ public DatatypeFeatures withFeatures(DatatypeFeature... features) { return _with(_enabledFor1, _explicitFor1, _enabledFor2 | mask, _explicitFor2 | mask, _enabledFor3, _explicitFor3); - case 3: + case 2: return _with(_enabledFor1, _explicitFor1, _enabledFor2, _explicitFor2, _enabledFor3 | mask, _explicitFor3 | mask); diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index 2dd51a5775..214e9ef5f0 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -13,10 +13,8 @@ import tools.jackson.core.*; import tools.jackson.core.util.DefaultPrettyPrinter; -import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.JavaTimeInitializer; import tools.jackson.databind.deser.*; import tools.jackson.databind.introspect.*; @@ -244,12 +242,6 @@ public abstract class MapperBuilder _javaTimeFeatures = - JacksonFeatureSet.fromDefaults(JavaTimeFeature.values()); - /* /********************************************************************** /* Transient state @@ -495,7 +487,6 @@ public boolean isEnabled(SerializationFeature f) { public boolean isEnabled(DatatypeFeature f) { return _datatypeFeatures.isEnabled(f); } - public boolean isEnabled(StreamReadFeature f) { return f.enabledIn(_streamReadFeatures); } @@ -503,14 +494,6 @@ public boolean isEnabled(StreamWriteFeature f) { return f.enabledIn(_streamWriteFeatures); } - public boolean isEnabled(JavaTimeFeature f) { - return _javaTimeFeatures.isEnabled(f); - } - - public JacksonFeatureSet getJavaTimeFeatures() { - return _javaTimeFeatures; - } - public DatatypeFeatures datatypeFeatures() { return _datatypeFeatures; } diff --git a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java index 9d6211d7b9..efce0cbef1 100644 --- a/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java +++ b/src/main/java/tools/jackson/databind/cfg/ModuleContextBase.java @@ -4,10 +4,9 @@ import java.util.function.UnaryOperator; import tools.jackson.core.*; -import tools.jackson.core.util.JacksonFeatureSet; + import tools.jackson.databind.*; import tools.jackson.databind.JacksonModule.SetupContext; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.deser.*; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.ser.SerializerFactory; @@ -128,15 +127,15 @@ public boolean isEnabled(StreamWriteFeature f) { } @Override - public boolean isEnabled(JavaTimeFeature f) { + public boolean isEnabled(DatatypeFeature f) { return _builder.isEnabled(f); } @Override - public JacksonFeatureSet getJavaTimeFeatures() { - return _builder.getJavaTimeFeatures(); + public DatatypeFeatures datatypeFeatures() { + return _builder.datatypeFeatures(); } - + /* /********************************************************************** /* Mutators for adding deserializers, related diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java index e23bf3d27a..5ab43e9aeb 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java @@ -1,11 +1,12 @@ package tools.jackson.databind.ext.datetime; -import tools.jackson.core.util.JacksonFeature; +import tools.jackson.databind.cfg.DatatypeFeature; +import tools.jackson.databind.cfg.DatatypeFeatures; /** * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). */ -public enum JavaTimeFeature implements JacksonFeature +public enum JavaTimeFeature implements DatatypeFeature { /** * Feature that determines whether {@link java.time.ZoneId} is normalized @@ -52,24 +53,29 @@ public enum JavaTimeFeature implements JacksonFeature ONE_BASED_MONTHS(false) ; + private final static int FEATURE_INDEX = DatatypeFeatures.FEATURE_INDEX_DATETIME; + /** * Whether feature is enabled or disabled by default. */ - private final boolean _defaultState; + private final boolean _enabledByDefault; private final int _mask; - JavaTimeFeature(boolean enabledByDefault) { - _defaultState = enabledByDefault; + private JavaTimeFeature(boolean enabledByDefault) { + _enabledByDefault = enabledByDefault; _mask = (1 << ordinal()); } @Override - public boolean enabledByDefault() { return _defaultState; } - + public boolean enabledByDefault() { return _enabledByDefault; } @Override public boolean enabledIn(int flags) { return (flags & _mask) != 0; } - @Override public int getMask() { return _mask; } + + @Override + public int featureIndex() { + return FEATURE_INDEX; + } } diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java index ff3c716321..079d455bc1 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java @@ -19,6 +19,7 @@ import java.time.*; import tools.jackson.databind.*; +import tools.jackson.databind.cfg.DatatypeFeatures; import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; import tools.jackson.databind.deser.std.StdValueInstantiator; @@ -84,21 +85,22 @@ public static JavaTimeInitializer getInstance() { private JavaTimeInitializer() { } public void setupModule(JacksonModule.SetupContext context) { + final DatatypeFeatures datatypeFeatures = context.datatypeFeatures(); context.addDeserializers(new SimpleDeserializers() // // Instant variants: .addDeserializer(Instant.class, - InstantDeserializer.INSTANT.withFeatures(context.getJavaTimeFeatures())) + InstantDeserializer.INSTANT.withFeatures(datatypeFeatures)) .addDeserializer(OffsetDateTime.class, - InstantDeserializer.OFFSET_DATE_TIME.withFeatures(context.getJavaTimeFeatures())) + InstantDeserializer.OFFSET_DATE_TIME.withFeatures(datatypeFeatures)) .addDeserializer(ZonedDateTime.class, - InstantDeserializer.ZONED_DATE_TIME.withFeatures(context.getJavaTimeFeatures())) + InstantDeserializer.ZONED_DATE_TIME.withFeatures(datatypeFeatures)) // // Other deserializers .addDeserializer(Duration.class, DurationDeserializer.INSTANCE) .addDeserializer(LocalDateTime.class, - LocalDateTimeDeserializer.INSTANCE.withFeatures(context.getJavaTimeFeatures())) + LocalDateTimeDeserializer.INSTANCE.withFeatures(datatypeFeatures)) .addDeserializer(LocalDate.class, - LocalDateDeserializer.INSTANCE.withFeatures(context.getJavaTimeFeatures())) + LocalDateDeserializer.INSTANCE.withFeatures(datatypeFeatures)) .addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE) .addDeserializer(MonthDay.class, MonthDayDeserializer.INSTANCE) .addDeserializer(OffsetTime.class, OffsetTimeDeserializer.INSTANCE) diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java index 4844e92679..2ec522f683 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java @@ -31,10 +31,10 @@ import tools.jackson.core.*; import tools.jackson.core.io.NumberInput; -import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.BeanProperty; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.cfg.DatatypeFeatures; import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.util.DecimalUtils; @@ -246,12 +246,9 @@ protected InstantDeserializer(InstantDeserializer base, _alwaysAllowStringifiedDateTimestamps = base._alwaysAllowStringifiedDateTimestamps; } - /** - * @since 2.16 - */ @SuppressWarnings("unchecked") protected InstantDeserializer(InstantDeserializer base, - JacksonFeatureSet features) + DatatypeFeatures features) { super((Class) base.handledType(), base._formatter); parsedToValue = base.parsedToValue; @@ -279,8 +276,7 @@ protected InstantDeserializer withLeniency(Boolean leniency) { return new InstantDeserializer<>(this, _formatter, leniency); } - // @since 2.16 - public InstantDeserializer withFeatures(JacksonFeatureSet features) { + public InstantDeserializer withFeatures(DatatypeFeatures features) { if ((_normalizeZoneId == features.isEnabled(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) && (_alwaysAllowStringifiedDateTimestamps == features.isEnabled(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) ) { diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java index cab6112586..8968b7792e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java @@ -24,14 +24,13 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.core.*; -import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.JavaType; import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; - +import tools.jackson.databind.cfg.DatatypeFeatures; import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** @@ -84,7 +83,7 @@ protected LocalDateDeserializer(LocalDateDeserializer base, JsonFormat.Shape sha /** * Since 2.19 */ - protected LocalDateDeserializer(LocalDateDeserializer base, JacksonFeatureSet features) { + protected LocalDateDeserializer(LocalDateDeserializer base, DatatypeFeatures features) { super(LocalDate.class, base._formatter); _useTimeZoneForLenientDateParsing = features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); } @@ -105,7 +104,7 @@ protected LocalDateDeserializer withLeniency(Boolean leniency) { /** * Since 2.19 */ - public LocalDateDeserializer withFeatures(JacksonFeatureSet features) { + public LocalDateDeserializer withFeatures(DatatypeFeatures features) { if (_useTimeZoneForLenientDateParsing == features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { return this; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java index a98f2f8d91..5a8fdc08fb 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java @@ -25,8 +25,8 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.core.*; -import tools.jackson.core.util.JacksonFeatureSet; import tools.jackson.databind.*; +import tools.jackson.databind.cfg.DatatypeFeatures; import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** @@ -97,7 +97,7 @@ protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, /** * Since 2.19 */ - protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, JacksonFeatureSet features) { + protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, DatatypeFeatures features) { super(LocalDateTime.class, base._formatter); _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; _useTimeZoneForLenientDateParsing = features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); @@ -131,7 +131,7 @@ protected JSR310DateTimeDeserializerBase _withFormatOverrides(Deserialization /** * Since 2.19 */ - public LocalDateTimeDeserializer withFeatures(JacksonFeatureSet features) { + public LocalDateTimeDeserializer withFeatures(DatatypeFeatures features) { if (_useTimeZoneForLenientDateParsing == features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { return this; diff --git a/src/main/java/tools/jackson/databind/json/JsonMapper.java b/src/main/java/tools/jackson/databind/json/JsonMapper.java index 1ce934d700..5583e87722 100644 --- a/src/main/java/tools/jackson/databind/json/JsonMapper.java +++ b/src/main/java/tools/jackson/databind/json/JsonMapper.java @@ -4,11 +4,11 @@ import tools.jackson.core.json.JsonFactory; import tools.jackson.core.json.JsonReadFeature; import tools.jackson.core.json.JsonWriteFeature; + import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.cfg.MapperBuilder; import tools.jackson.databind.cfg.MapperBuilderState; import tools.jackson.databind.cfg.PackageVersion; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** * JSON-specific {@link ObjectMapper} implementation. @@ -91,20 +91,6 @@ public Builder disable(JsonWriteFeature... features) { return this; } - public Builder enable(JavaTimeFeature... features) { - for (JavaTimeFeature f : features) { - _javaTimeFeatures = _javaTimeFeatures.with(f); - } - return this; - } - - public Builder disable(JavaTimeFeature... features) { - for (JavaTimeFeature f : features) { - _javaTimeFeatures = _javaTimeFeatures.without(f); - } - return this; - } - public Builder configure(JsonWriteFeature feature, boolean state) { if (state) { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java index 53dc358c95..a34f1cc9b9 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java @@ -9,7 +9,6 @@ import tools.jackson.core.json.JsonWriteFeature; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ser.LocalDateSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.deser.LocalDateDeserializer; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java index 8e9889dd76..bf840f8007 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java @@ -8,7 +8,6 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ser.LocalDateTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.deser.LocalDateTimeDeserializer; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java index 9def13b682..557a9654d6 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java @@ -5,7 +5,6 @@ import java.util.stream.Stream; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ser.LocalTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.deser.LocalTimeDeserializer; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java index 455accb594..b936cd1ba3 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java @@ -8,7 +8,6 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ser.YearMonthSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.deser.YearMonthDeserializer; @@ -16,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class TestYearMonthSerializationWithCustomFormatter { - +public class TestYearMonthSerializationWithCustomFormatter +{ @ParameterizedTest @MethodSource("customFormatters") void testSerialization(DateTimeFormatter formatter) throws Exception { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java index 18c432288f..8733c1a8e5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java @@ -8,7 +8,6 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.ser.YearSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.datetime.deser.YearDeserializer; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java index 4d6d8732e0..34c8b40234 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java @@ -11,14 +11,13 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.ser.ZonedDateTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import static org.junit.jupiter.api.Assertions.assertTrue; -public class TestZonedDateTimeSerializationWithCustomFormatter { - +public class TestZonedDateTimeSerializationWithCustomFormatter +{ @MethodSource("customFormatters") @ParameterizedTest public void testSerialization(DateTimeFormatter formatter) throws Exception { From 643262767a448c012fba7ddb2a4e14dcbed217ca Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 15:34:40 -0700 Subject: [PATCH 17/21] Minor refactoring --- .../java/tools/jackson/databind/cfg/DatatypeFeatures.java | 5 +---- .../databind/{ext/datetime => cfg}/JavaTimeFeature.java | 8 ++++---- .../databind/ext/datetime/JavaTimeInitializer.java | 1 + .../databind/ext/datetime/deser/InstantDeserializer.java | 2 +- .../ext/datetime/deser/LocalDateDeserializer.java | 2 +- .../ext/datetime/deser/LocalDateTimeDeserializer.java | 2 +- .../databind/ext/datetime/deser/InstantDeser291Test.java | 2 +- .../databind/ext/datetime/deser/LocalDateDeserTest.java | 2 +- .../ext/datetime/deser/LocalDateTimeDeserTest.java | 2 +- .../ext/datetime/deser/OneBasedMonthDeserTest.java | 2 +- .../ext/datetime/deser/ZonedDateTimeDeserTest.java | 2 +- .../databind/ext/datetime/ser/OneBasedMonthSerTest.java | 3 +-- .../databind/ext/datetime/ser/ZonedDateTimeSerTest.java | 2 +- 13 files changed, 16 insertions(+), 19 deletions(-) rename src/main/java/tools/jackson/databind/{ext/datetime => cfg}/JavaTimeFeature.java (91%) diff --git a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java index 2eadb97147..66785de8d7 100644 --- a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java +++ b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java @@ -2,7 +2,6 @@ import tools.jackson.core.util.JacksonFeature; import tools.jackson.core.util.VersionUtil; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; /** * Immutable value class that contains settings for multiple @@ -15,9 +14,7 @@ public class DatatypeFeatures protected final static int FEATURE_INDEX_ENUM = 0; protected final static int FEATURE_INDEX_JSON_NODE = 1; - - // !!! TODO: make protected - public final static int FEATURE_INDEX_DATETIME = 2; + protected final static int FEATURE_INDEX_DATETIME = 2; private final int _enabledFor1, _enabledFor2, _enabledFor3; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java similarity index 91% rename from src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java rename to src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java index 5ab43e9aeb..05e81377fa 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java @@ -1,7 +1,6 @@ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.cfg; -import tools.jackson.databind.cfg.DatatypeFeature; -import tools.jackson.databind.cfg.DatatypeFeatures; +import tools.jackson.databind.ext.datetime.JavaTimeInitializer; /** * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). @@ -35,7 +34,8 @@ public enum JavaTimeFeature implements DatatypeFeature * Feature that controls whether stringified numbers (Strings that without * quotes would be legal JSON Numbers) may be interpreted as * timestamps (enabled) or not (disabled), in case where there is an - * explicitly defined pattern ({@code DateTimeFormatter}) for value. + * explicitly defined pattern ({@code DateTimeFormatter}, usually by + * using {@code @JsonFormat} annotation) for value. *

    * Note that when the default pattern is used (no custom pattern defined), * stringified numbers are always accepted as timestamps regardless of diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java index 079d455bc1..ceef0bfe6e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java @@ -20,6 +20,7 @@ import tools.jackson.databind.*; import tools.jackson.databind.cfg.DatatypeFeatures; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; import tools.jackson.databind.deser.std.StdValueInstantiator; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java index 2ec522f683..a85af4d8a3 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java @@ -35,7 +35,7 @@ import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.ext.datetime.util.DecimalUtils; /** diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java index 8968b7792e..8f1e688d36 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java @@ -31,7 +31,7 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.cfg.JavaTimeFeature; /** * Deserializer for Java 8 temporal {@link LocalDate}s. diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java index 5a8fdc08fb..23d1fe3e77 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java @@ -27,7 +27,7 @@ import tools.jackson.core.*; import tools.jackson.databind.*; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; +import tools.jackson.databind.cfg.JavaTimeFeature; /** * Deserializer for Java 8 temporal {@link LocalDateTime}s. diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java index 43ad05cee5..e3be144f1c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java @@ -6,8 +6,8 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java index f2579e8e10..a11b9a9e80 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java @@ -16,13 +16,13 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.*; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; import tools.jackson.databind.ext.datetime.DateTimeTestBase; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java index 0915222fd1..b43c531c60 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java @@ -35,11 +35,11 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.*; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.deser.DeserializationProblemHandler; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; import tools.jackson.databind.ext.datetime.DateTimeTestBase; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java index d6420dd7b6..6be0d599f2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java @@ -13,10 +13,10 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; import tools.jackson.databind.ext.datetime.DateTimeTestBase; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java index a1ee0da4bb..350172b761 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java @@ -16,9 +16,9 @@ import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java index d8de2dd87d..7b7f422041 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java @@ -6,9 +6,8 @@ import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.json.JsonMapper; - -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java index 14e8574a30..8e7244a7aa 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java @@ -32,9 +32,9 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.JavaTimeFeature; import tools.jackson.databind.ext.datetime.MockObjectConfiguration; import tools.jackson.databind.ext.datetime.DateTimeTestBase; import tools.jackson.databind.ext.datetime.util.DecimalUtils; From 6e25e806d82d6561c9554c4c3dbd39d8d5b15f4f Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 15:37:53 -0700 Subject: [PATCH 18/21] Refactoring: change "ext.datetime" to "ext.javatime" wrt supported API name --- src/main/java/module-info.java | 10 +++++----- .../jackson/databind/cfg/JavaTimeFeature.java | 2 +- .../jackson/databind/cfg/MapperBuilder.java | 2 +- .../JavaTimeInitializer.java | 11 +++++------ .../deser/DurationDeserializer.java | 6 +++--- .../deser/InstantDeserializer.java | 4 ++-- .../deser/JSR310DateTimeDeserializerBase.java | 2 +- .../deser/JSR310DeserializerBase.java | 2 +- .../deser/JSR310StringParsableDeserializer.java | 2 +- .../deser/JavaTimeDeserializerModifier.java | 2 +- .../deser/LocalDateDeserializer.java | 2 +- .../deser/LocalDateTimeDeserializer.java | 2 +- .../deser/LocalTimeDeserializer.java | 2 +- .../deser/MonthDayDeserializer.java | 2 +- .../deser/OffsetTimeDeserializer.java | 2 +- .../deser/OneBasedMonthDeserializer.java | 2 +- .../deser/YearDeserializer.java | 2 +- .../deser/YearMonthDeserializer.java | 2 +- .../deser/key/DurationKeyDeserializer.java | 2 +- .../deser/key/InstantKeyDeserializer.java | 2 +- .../deser/key/Jsr310KeyDeserializer.java | 2 +- .../deser/key/LocalDateKeyDeserializer.java | 2 +- .../deser/key/LocalDateTimeKeyDeserializer.java | 2 +- .../deser/key/LocalTimeKeyDeserializer.java | 2 +- .../deser/key/MonthDayKeyDeserializer.java | 2 +- .../deser/key/OffsetDateTimeKeyDeserializer.java | 2 +- .../deser/key/OffsetTimeKeyDeserializer.java | 2 +- .../deser/key/PeriodKeyDeserializer.java | 2 +- .../deser/key/YearKeyDeserializer.java | 2 +- .../deser/key/YearMonthKeyDeserializer.java | 2 +- .../deser/key/ZoneIdKeyDeserializer.java | 2 +- .../deser/key/ZoneOffsetKeyDeserializer.java | 2 +- .../deser/key/ZonedDateTimeKeyDeserializer.java | 2 +- .../ser/DurationSerializer.java | 6 +++--- .../ser/InstantSerializer.java | 2 +- .../ser/InstantSerializerBase.java | 4 ++-- .../ser/JSR310FormattedSerializerBase.java | 2 +- .../ser/JSR310SerializerBase.java | 2 +- .../ser/JavaTimeSerializerModifier.java | 2 +- .../ser/LocalDateSerializer.java | 2 +- .../ser/LocalDateTimeSerializer.java | 2 +- .../ser/LocalTimeSerializer.java | 2 +- .../ser/MonthDaySerializer.java | 2 +- .../ser/OffsetDateTimeSerializer.java | 2 +- .../ser/OffsetTimeSerializer.java | 2 +- .../ser/OneBasedMonthSerializer.java | 2 +- .../ser/YearMonthSerializer.java | 2 +- .../ser/YearSerializer.java | 2 +- .../ser/ZoneIdSerializer.java | 2 +- .../ser/ZonedDateTimeSerializer.java | 2 +- .../ser/key/ZonedDateTimeKeySerializer.java | 4 ++-- .../util/DecimalUtils.java | 2 +- .../util/DurationUnitConverter.java | 4 ++-- src/test/java/module-info.java | 16 ++++++++-------- .../{datetime => javatime}/DateTimeTestBase.java | 2 +- .../MockObjectConfiguration.java | 2 +- .../{datetime => javatime}/TestDecimalUtils.java | 4 ++-- .../ext/{datetime => javatime}/TestFeatures.java | 2 +- .../deser/DefaultTypingTest.java | 4 ++-- .../deser/DurationDeser337Test.java | 5 ++--- .../deser/DurationDeserTest.java | 6 +++--- .../deser/InstantDeser291Test.java | 4 ++-- .../deser/InstantDeserTest.java | 10 +++++----- .../deser/LocalDateDeserTest.java | 6 +++--- .../deser/LocalDateTimeDeserTest.java | 6 +++--- .../deser/LocalTimeDeserTest.java | 6 +++--- .../deser/MonthDayDeserTest.java | 6 +++--- .../deser/OffsetDateTimeDeserTest.java | 8 ++++---- .../deser/OffsetTimeDeserTest.java | 6 +++--- .../deser/OneBasedMonthDeserTest.java | 6 +++--- .../deser/PeriodDeserTest.java | 6 +++--- .../deser/YearDeserTest.java | 6 +++--- .../deser/YearMonthDeserTest.java | 4 ++-- .../deser/ZoneIdDeserTest.java | 6 +++--- .../deser/ZoneOffsetDeserTest.java | 6 +++--- .../deser/ZonedDateTimeDeserTest.java | 4 ++-- .../key/ZonedDateTimeKeyDeserializerTest.java | 4 ++-- .../key/DurationAsKeyTest.java | 4 ++-- .../key/InstantAsKeyTest.java | 4 ++-- .../key/LocalDateAsKeyTest.java | 4 ++-- .../key/LocalDateTimeAsKeyTest.java | 4 ++-- .../key/LocalTimeAsKeyTest.java | 4 ++-- .../key/MonthDayAsKeyTest.java | 4 ++-- .../key/OffsetDateTimeAsKeyTest.java | 4 ++-- .../key/OffsetTimeAsKeyTest.java | 4 ++-- .../key/PeriodAsKeyTest.java | 4 ++-- .../key/YearAsKeyTest.java | 4 ++-- .../key/YearMonthAsKeyTest.java | 4 ++-- .../key/ZoneIdAsKeyTest.java | 4 ++-- .../key/ZoneOffsetAsKeyTest.java | 4 ++-- .../key/ZonedDateTimeAsKeyTest.java | 4 ++-- .../misc/DateTimeExceptionTest.java | 4 ++-- .../misc/DateTimeSchemasTest.java | 4 ++-- .../misc/DeductionTypeSerialization296Test.java | 5 ++--- .../misc/JDKSerializabilityTest.java | 4 ++-- .../misc/UnsupportedTypesTest.java | 4 ++-- .../ser/DurationSerTest.java | 6 +++--- .../ser/InstantSerTest.java | 8 ++++---- .../ser/LocalDateSerTest.java | 6 +++--- .../ser/LocalDateTimeSerTest.java | 6 +++--- .../ser/LocalTimeSerTest.java | 7 ++++--- .../ser/MonthDaySerTest.java | 6 +++--- .../ser/OffsetDateTimeSerTest.java | 8 ++++---- .../ser/OffsetTimeSerTest.java | 6 +++--- .../ser/OneBasedMonthSerTest.java | 4 ++-- .../ser/PeriodSerTest.java | 6 +++--- ...ocalDateSerializationWithCustomFormatter.java | 5 +++-- ...DateTimeSerializationWithCustomFormatter.java | 5 +++-- ...ocalTimeSerializationWithCustomFormatter.java | 5 +++-- ...earMonthSerializationWithCustomFormatter.java | 5 +++-- ...TestYearSerializationWithCustomFormatter.java | 5 +++-- ...DateTimeSerializationWithCustomFormatter.java | 3 ++- .../ser/WriteNanosecondsTest.java | 4 ++-- .../ser/WriteZoneIdTest.java | 6 +++--- .../ser/YearMonthSerializationTest.java | 6 +++--- .../{datetime => javatime}/ser/YearSerTest.java | 6 +++--- .../ser/ZoneIdSerTest.java | 6 +++--- .../ser/ZoneOffsetSerTest.java | 6 +++--- .../ser/ZonedDateTimeSerTest.java | 9 +++++---- .../ZonedDateTimeSerWithJsonFormat333Test.java | 4 ++-- .../InstantDeserializerNegative359Test.java | 4 ++-- .../tofix/InstantViaBigDecimal307Test.java | 4 ++-- .../tofix/OffsetDateTimeDeser279Test.java | 4 ++-- .../tofix/ZonedDateTimeIssue244Test.java | 4 ++-- .../util/DurationUnitConverterTest.java | 5 +++-- 125 files changed, 258 insertions(+), 252 deletions(-) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/JavaTimeInitializer.java (97%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/DurationDeserializer.java (97%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/InstantDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/JSR310DateTimeDeserializerBase.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/JSR310DeserializerBase.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/JSR310StringParsableDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/JavaTimeDeserializerModifier.java (94%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalDateDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalDateTimeDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalTimeDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/MonthDayDeserializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/OffsetTimeDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/OneBasedMonthDeserializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/YearDeserializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/YearMonthDeserializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/DurationKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/InstantKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/Jsr310KeyDeserializer.java (96%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/LocalDateKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/LocalDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/LocalTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/MonthDayKeyDeserializer.java (95%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/OffsetDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/OffsetTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/PeriodKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/YearKeyDeserializer.java (94%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/YearMonthKeyDeserializer.java (95%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/ZoneIdKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/ZoneOffsetKeyDeserializer.java (92%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/ZonedDateTimeKeyDeserializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/DurationSerializer.java (97%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/InstantSerializer.java (97%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/InstantSerializerBase.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/JSR310FormattedSerializerBase.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/JSR310SerializerBase.java (96%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/JavaTimeSerializerModifier.java (94%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalDateSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalDateTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalTimeSerializer.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/MonthDaySerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OffsetDateTimeSerializer.java (97%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OffsetTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OneBasedMonthSerializer.java (95%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/YearMonthSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/YearSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZoneIdSerializer.java (95%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZonedDateTimeSerializer.java (98%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/ser/key/ZonedDateTimeKeySerializer.java (93%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/util/DecimalUtils.java (99%) rename src/main/java/tools/jackson/databind/ext/{datetime => javatime}/util/DurationUnitConverter.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/DateTimeTestBase.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/MockObjectConfiguration.java (82%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/TestDecimalUtils.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/TestFeatures.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/DefaultTypingTest.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/DurationDeser337Test.java (92%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/DurationDeserTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/InstantDeser291Test.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/InstantDeserTest.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalDateDeserTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalDateTimeDeserTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/LocalTimeDeserTest.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/MonthDayDeserTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/OffsetDateTimeDeserTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/OffsetTimeDeserTest.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/OneBasedMonthDeserTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/PeriodDeserTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/YearDeserTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/YearMonthDeserTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/ZoneIdDeserTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/ZoneOffsetDeserTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/ZonedDateTimeDeserTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/deser/key/ZonedDateTimeKeyDeserializerTest.java (95%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/DurationAsKeyTest.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/InstantAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/LocalDateAsKeyTest.java (89%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/LocalDateTimeAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/LocalTimeAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/MonthDayAsKeyTest.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/OffsetDateTimeAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/OffsetTimeAsKeyTest.java (95%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/PeriodAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/YearAsKeyTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/YearMonthAsKeyTest.java (89%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/ZoneIdAsKeyTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/ZoneOffsetAsKeyTest.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/key/ZonedDateTimeAsKeyTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/misc/DateTimeExceptionTest.java (85%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/misc/DateTimeSchemasTest.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/misc/DeductionTypeSerialization296Test.java (95%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/misc/JDKSerializabilityTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/misc/UnsupportedTypesTest.java (88%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/DurationSerTest.java (98%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/InstantSerTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalDateSerTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalDateTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/LocalTimeSerTest.java (96%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/MonthDaySerTest.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OffsetDateTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OffsetTimeSerTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/OneBasedMonthSerTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/PeriodSerTest.java (90%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestLocalDateSerializationWithCustomFormatter.java (92%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestLocalDateTimeSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestLocalTimeSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestYearMonthSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestYearSerializationWithCustomFormatter.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/TestZonedDateTimeSerializationWithCustomFormatter.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/WriteNanosecondsTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/WriteZoneIdTest.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/YearMonthSerializationTest.java (97%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/YearSerTest.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZoneIdSerTest.java (90%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZoneOffsetSerTest.java (92%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZonedDateTimeSerTest.java (99%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/ser/ZonedDateTimeSerWithJsonFormat333Test.java (92%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/tofix/InstantDeserializerNegative359Test.java (91%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/tofix/InstantViaBigDecimal307Test.java (93%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/tofix/OffsetDateTimeDeser279Test.java (92%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/tofix/ZonedDateTimeIssue244Test.java (94%) rename src/test/java/tools/jackson/databind/ext/{datetime => javatime}/util/DurationUnitConverterTest.java (88%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 14a3b0e20f..3e63715106 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -35,11 +35,11 @@ exports tools.jackson.databind.deser.std; exports tools.jackson.databind.exc; exports tools.jackson.databind.ext; - exports tools.jackson.databind.ext.datetime; - exports tools.jackson.databind.ext.datetime.deser; - exports tools.jackson.databind.ext.datetime.deser.key; - exports tools.jackson.databind.ext.datetime.ser; - exports tools.jackson.databind.ext.datetime.ser.key; + exports tools.jackson.databind.ext.javatime; + exports tools.jackson.databind.ext.javatime.deser; + exports tools.jackson.databind.ext.javatime.deser.key; + exports tools.jackson.databind.ext.javatime.ser; + exports tools.jackson.databind.ext.javatime.ser.key; exports tools.jackson.databind.ext.jdk8; // Needed by Ion module for SqlDate deserializer: exports tools.jackson.databind.ext.sql; diff --git a/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java index 05e81377fa..b27588f63d 100644 --- a/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java @@ -1,6 +1,6 @@ package tools.jackson.databind.cfg; -import tools.jackson.databind.ext.datetime.JavaTimeInitializer; +import tools.jackson.databind.ext.javatime.JavaTimeInitializer; /** * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). diff --git a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java index 214e9ef5f0..063d0b2c48 100644 --- a/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java +++ b/src/main/java/tools/jackson/databind/cfg/MapperBuilder.java @@ -15,8 +15,8 @@ import tools.jackson.core.util.DefaultPrettyPrinter; import tools.jackson.core.util.Snapshottable; import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.JavaTimeInitializer; import tools.jackson.databind.deser.*; +import tools.jackson.databind.ext.javatime.JavaTimeInitializer; import tools.jackson.databind.introspect.*; import tools.jackson.databind.jsontype.*; import tools.jackson.databind.jsontype.impl.DefaultTypeResolverBuilder; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java index ceef0bfe6e..945826e3c9 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.ext.javatime; import java.time.*; @@ -24,9 +24,10 @@ import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; import tools.jackson.databind.deser.std.StdValueInstantiator; -import tools.jackson.databind.ext.datetime.deser.*; -import tools.jackson.databind.ext.datetime.deser.key.*; -import tools.jackson.databind.ext.datetime.ser.*; +import tools.jackson.databind.ext.javatime.deser.*; +import tools.jackson.databind.ext.javatime.deser.key.*; +import tools.jackson.databind.ext.javatime.ser.*; +import tools.jackson.databind.ext.javatime.ser.key.ZonedDateTimeKeySerializer; import tools.jackson.databind.introspect.AnnotatedClass; import tools.jackson.databind.introspect.AnnotatedClassResolver; import tools.jackson.databind.introspect.AnnotatedMethod; @@ -35,8 +36,6 @@ import tools.jackson.databind.module.SimpleSerializers; import tools.jackson.databind.ser.std.ToStringSerializer; -import tools.jackson.databind.ext.datetime.ser.key.ZonedDateTimeKeySerializer; - /** * Class that registers capability of serializing {@code java.time} objects with the Jackson core. *

    diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/DurationDeserializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/DurationDeserializer.java index c2e39f9cbc..ec6e87975e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/DurationDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/DurationDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.math.BigDecimal; import java.time.DateTimeException; @@ -29,8 +29,8 @@ import tools.jackson.core.StreamReadCapability; import tools.jackson.core.io.NumberInput; import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; -import tools.jackson.databind.ext.datetime.util.DurationUnitConverter; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.util.DurationUnitConverter; /** * Deserializer for Java 8 temporal {@link Duration}s. diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java index a85af4d8a3..b7c07b20b6 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/InstantDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.math.BigDecimal; import java.time.*; @@ -36,7 +36,7 @@ import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.cfg.DatatypeFeatures; import tools.jackson.databind.cfg.JavaTimeFeature; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; /** * Deserializer for Java 8 temporal {@link Instant}s, {@link OffsetDateTime}, diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DateTimeDeserializerBase.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DateTimeDeserializerBase.java index 668ea8ef6c..2a61db5d65 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DateTimeDeserializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DateTimeDeserializerBase.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DeserializerBase.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DeserializerBase.java index 67b534d218..23ca16f384 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310DeserializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310DeserializerBase.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310StringParsableDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310StringParsableDeserializer.java index f77a7fabd2..2cb31ed3ea 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/JSR310StringParsableDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/JSR310StringParsableDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.Period; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/JavaTimeDeserializerModifier.java similarity index 94% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/JavaTimeDeserializerModifier.java index 6a3fb92526..7976888128 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/JavaTimeDeserializerModifier.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/JavaTimeDeserializerModifier.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Month; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java index 8f1e688d36..8cb452000b 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.Instant; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserializer.java index 23d1fe3e77..3cf96dd3db 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.Instant; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserializer.java index f1c9a586e7..131a1ff569 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.LocalTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserializer.java index b33d6d7f9c..9f7b3df94b 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.MonthDay; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserializer.java index 5a1b205c29..436f633b51 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.OffsetTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserializer.java index f0b21a6318..6509c72423 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Month; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/YearDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/YearDeserializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/YearDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/YearDeserializer.java index 68738260b2..041a64866e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/YearDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/YearDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.Year; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserializer.java index 3b86a2fe37..d5ae40118c 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.DateTimeException; import java.time.YearMonth; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/DurationKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/DurationKeyDeserializer.java similarity index 92% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/DurationKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/DurationKeyDeserializer.java index ea7694b9c9..75434b3b95 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/DurationKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/DurationKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.Duration; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/InstantKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/InstantKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/InstantKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/InstantKeyDeserializer.java index 3c2511ad7d..995772514e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/InstantKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/InstantKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.Instant; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/Jsr310KeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/Jsr310KeyDeserializer.java similarity index 96% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/Jsr310KeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/Jsr310KeyDeserializer.java index 4711a63b86..33144b1c82 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/Jsr310KeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/Jsr310KeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateKeyDeserializer.java index 52195cdb4a..944ac72f8f 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.LocalDate; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateTimeKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateTimeKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateTimeKeyDeserializer.java index d3019c56ec..0e4dacaad6 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalDateTimeKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalDateTimeKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.LocalDateTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalTimeKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalTimeKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalTimeKeyDeserializer.java index 7e8e1fc595..7e327e01bc 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/LocalTimeKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/LocalTimeKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.LocalTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/MonthDayKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/MonthDayKeyDeserializer.java similarity index 95% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/MonthDayKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/MonthDayKeyDeserializer.java index e58890fb6b..c8ff90150f 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/MonthDayKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/MonthDayKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetDateTimeKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetDateTimeKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetDateTimeKeyDeserializer.java index 609b1705be..398c33979c 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetDateTimeKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetDateTimeKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.OffsetDateTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetTimeKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetTimeKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetTimeKeyDeserializer.java index a08a7b52a1..7f82cb916a 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/OffsetTimeKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/OffsetTimeKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.OffsetTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/PeriodKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/PeriodKeyDeserializer.java similarity index 92% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/PeriodKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/PeriodKeyDeserializer.java index 7c00360883..19c87fcc5d 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/PeriodKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/PeriodKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.Period; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearKeyDeserializer.java similarity index 94% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearKeyDeserializer.java index 98316c6c28..1aea9c5693 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.Year; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearMonthKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearMonthKeyDeserializer.java similarity index 95% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearMonthKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearMonthKeyDeserializer.java index 10ac089477..bde36b2393 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/YearMonthKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/YearMonthKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneIdKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneIdKeyDeserializer.java similarity index 92% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneIdKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneIdKeyDeserializer.java index d8d1dd7ddc..9519672794 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneIdKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneIdKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.ZoneId; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneOffsetKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneOffsetKeyDeserializer.java similarity index 92% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneOffsetKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneOffsetKeyDeserializer.java index 1010416e5d..a8812bee1a 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZoneOffsetKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZoneOffsetKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.ZoneOffset; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializer.java index 85c64614c3..af56212b02 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.DateTimeException; import java.time.ZonedDateTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/DurationSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/DurationSerializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/DurationSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/DurationSerializer.java index 5af293138d..00fb9db912 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/DurationSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/DurationSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.math.BigDecimal; import java.time.Duration; @@ -27,11 +27,11 @@ import tools.jackson.core.JsonToken; import tools.jackson.databind.*; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.util.DurationUnitConverter; import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import tools.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; -import tools.jackson.databind.ext.datetime.util.DurationUnitConverter; /** * Serializer for Java 8 temporal {@link Duration}s. diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializer.java index bc3c7bc898..5b8b401b08 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import com.fasterxml.jackson.annotation.JsonFormat; import java.time.Instant; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializerBase.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializerBase.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializerBase.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializerBase.java index 74bba6856a..57f9e381cc 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/InstantSerializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/InstantSerializerBase.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; @@ -29,11 +29,11 @@ import tools.jackson.core.JsonParser.NumberType; import tools.jackson.databind.*; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; import tools.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor; import tools.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor; import tools.jackson.databind.jsonFormatVisitors.JsonValueFormat; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; /** * Base class for serializers used for {@link java.time.Instant} and diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310FormattedSerializerBase.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310FormattedSerializerBase.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310FormattedSerializerBase.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310FormattedSerializerBase.java index f56b72d9fc..32c2a6311a 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310FormattedSerializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310FormattedSerializerBase.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.format.DateTimeFormatter; import java.util.List; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310SerializerBase.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310SerializerBase.java similarity index 96% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310SerializerBase.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310SerializerBase.java index 952d285c79..0c208768b7 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/JSR310SerializerBase.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/JSR310SerializerBase.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import tools.jackson.core.JacksonException; import tools.jackson.core.JsonGenerator; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/JavaTimeSerializerModifier.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/JavaTimeSerializerModifier.java similarity index 94% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/JavaTimeSerializerModifier.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/JavaTimeSerializerModifier.java index 0bd3990d3f..33c0b12ffe 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/JavaTimeSerializerModifier.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/JavaTimeSerializerModifier.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Month; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerializer.java index fcbba6bfed..ba1d4b2879 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDate; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerializer.java index d330da5567..540202a2ec 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerializer.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerializer.java index d3ee115474..8ae9a92428 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerializer.java index 5593d0fe4f..e2124940ff 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.MonthDay; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerializer.java similarity index 97% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerializer.java index 199b051034..968e8f9ec7 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import com.fasterxml.jackson.annotation.JsonFormat; import java.time.OffsetDateTime; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerializer.java index ffb76793ad..46dadbbdce 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.OffsetTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerializer.java similarity index 95% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerializer.java index 442146b515..f2680cf18e 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Month; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializer.java index e6df1e10f7..32659a1275 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.YearMonth; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/YearSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/YearSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/YearSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/YearSerializer.java index 349803dac5..302591df3a 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/YearSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/YearSerializer.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Year; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerializer.java similarity index 95% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerializer.java index 731f43e260..af75de87ca 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZoneId; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerializer.java similarity index 98% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerializer.java index bc32fcf00f..8240cac337 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/ser/key/ZonedDateTimeKeySerializer.java b/src/main/java/tools/jackson/databind/ext/javatime/ser/key/ZonedDateTimeKeySerializer.java similarity index 93% rename from src/main/java/tools/jackson/databind/ext/datetime/ser/key/ZonedDateTimeKeySerializer.java rename to src/main/java/tools/jackson/databind/ext/javatime/ser/key/ZonedDateTimeKeySerializer.java index bf2e457a72..3c0ef465a3 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/ser/key/ZonedDateTimeKeySerializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/ser/key/ZonedDateTimeKeySerializer.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser.key; +package tools.jackson.databind.ext.javatime.ser.key; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -7,7 +7,7 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import tools.jackson.databind.SerializationContext; import tools.jackson.databind.SerializationFeature; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/util/DecimalUtils.java b/src/main/java/tools/jackson/databind/ext/javatime/util/DecimalUtils.java similarity index 99% rename from src/main/java/tools/jackson/databind/ext/datetime/util/DecimalUtils.java rename to src/main/java/tools/jackson/databind/ext/javatime/util/DecimalUtils.java index 74cebbca9d..67634e9476 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/util/DecimalUtils.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/util/DecimalUtils.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.util; +package tools.jackson.databind.ext.javatime.util; import tools.jackson.core.io.NumberInput; diff --git a/src/main/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverter.java b/src/main/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverter.java similarity index 96% rename from src/main/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverter.java rename to src/main/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverter.java index 8f892f78c9..6c85a47b90 100644 --- a/src/main/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverter.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverter.java @@ -1,6 +1,6 @@ -package tools.jackson.databind.ext.datetime.util; +package tools.jackson.databind.ext.javatime.util; -import static tools.jackson.databind.ext.datetime.util.DurationUnitConverter.DurationSerialization.deserializer; +import static tools.jackson.databind.ext.javatime.util.DurationUnitConverter.DurationSerialization.deserializer; import java.time.Duration; import java.time.temporal.ChronoUnit; diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index a9a3f3ca2a..03045f6b14 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -35,14 +35,14 @@ opens tools.jackson.databind.deser.jdk; opens tools.jackson.databind.deser.std; opens tools.jackson.databind.exc; - opens tools.jackson.databind.ext.datetime; - opens tools.jackson.databind.ext.datetime.deser; - opens tools.jackson.databind.ext.datetime.deser.key; - opens tools.jackson.databind.ext.datetime.key; - opens tools.jackson.databind.ext.datetime.misc; - opens tools.jackson.databind.ext.datetime.ser; - opens tools.jackson.databind.ext.datetime.tofix; - opens tools.jackson.databind.ext.datetime.util; + opens tools.jackson.databind.ext.javatime; + opens tools.jackson.databind.ext.javatime.deser; + opens tools.jackson.databind.ext.javatime.deser.key; + opens tools.jackson.databind.ext.javatime.key; + opens tools.jackson.databind.ext.javatime.misc; + opens tools.jackson.databind.ext.javatime.ser; + opens tools.jackson.databind.ext.javatime.tofix; + opens tools.jackson.databind.ext.javatime.util; opens tools.jackson.databind.introspect; opens tools.jackson.databind.json; opens tools.jackson.databind.jsonFormatVisitors; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java b/src/test/java/tools/jackson/databind/ext/javatime/DateTimeTestBase.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java rename to src/test/java/tools/jackson/databind/ext/javatime/DateTimeTestBase.java index 34a651e28a..7611d526d2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/DateTimeTestBase.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/DateTimeTestBase.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.ext.javatime; import java.time.ZoneId; import java.util.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java b/src/test/java/tools/jackson/databind/ext/javatime/MockObjectConfiguration.java similarity index 82% rename from src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java rename to src/test/java/tools/jackson/databind/ext/javatime/MockObjectConfiguration.java index 1ea765938b..6484240ca3 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/MockObjectConfiguration.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/MockObjectConfiguration.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.ext.javatime; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java b/src/test/java/tools/jackson/databind/ext/javatime/TestDecimalUtils.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java rename to src/test/java/tools/jackson/databind/ext/javatime/TestDecimalUtils.java index b490345118..d369823a4a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/TestDecimalUtils.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/TestDecimalUtils.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.ext.javatime; import java.math.BigDecimal; import java.util.concurrent.TimeUnit; @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/TestFeatures.java b/src/test/java/tools/jackson/databind/ext/javatime/TestFeatures.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/TestFeatures.java rename to src/test/java/tools/jackson/databind/ext/javatime/TestFeatures.java index 882323036d..ff0fd25638 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/TestFeatures.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/TestFeatures.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime; +package tools.jackson.databind.ext.javatime; import org.junit.jupiter.api.Test; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/DefaultTypingTest.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/DefaultTypingTest.java index dedf2a45dd..df6fb8c1c2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DefaultTypingTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/DefaultTypingTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.ZoneId; @@ -7,8 +7,8 @@ import tools.jackson.databind.DatabindContext; import tools.jackson.databind.JavaType; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.jsontype.PolymorphicTypeValidator; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeser337Test.java similarity index 92% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeser337Test.java index 09c8e39354..b11a5fffd8 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeser337Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeser337Test.java @@ -1,12 +1,11 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Duration; import org.junit.jupiter.api.Test; import tools.jackson.databind.*; - -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeserTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeserTest.java index 9b290eeebe..d6a76f7e62 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/DurationDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/DurationDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.math.BigInteger; import java.time.Duration; @@ -20,8 +20,8 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.InvalidDefinitionException; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java index e3be144f1c..a4c47cec5c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Instant; import java.util.Locale; @@ -7,8 +7,8 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeserTest.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeserTest.java index a7c187b900..fe77ce6633 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/InstantDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.*; import java.time.format.DateTimeFormatter; @@ -13,9 +13,9 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import com.fasterxml.jackson.annotation.JsonFormat; @@ -25,7 +25,7 @@ import tools.jackson.databind.SerializationFeature; import static org.junit.jupiter.api.Assertions.*; -import static tools.jackson.databind.ext.datetime.deser.InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX; +import static tools.jackson.databind.ext.javatime.deser.InstantDeserializer.ISO8601_COLONLESS_OFFSET_REGEX; public class InstantDeserTest extends DateTimeTestBase { diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java index a11b9a9e80..b6a09cdaf3 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.*; import java.time.temporal.Temporal; @@ -22,9 +22,9 @@ import tools.jackson.databind.*; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java index b43c531c60..2c39dba1ba 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.io.IOException; import java.time.LocalDateTime; @@ -39,9 +39,9 @@ import tools.jackson.databind.deser.DeserializationProblemHandler; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserTest.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserTest.java index 7fc9ce75e0..da8c801086 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/LocalTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalTimeDeserTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.LocalTime; import java.time.temporal.Temporal; @@ -33,8 +33,8 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserTest.java index 62449cff2a..b70a03c448 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/MonthDayDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/MonthDayDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Month; import java.time.MonthDay; @@ -15,8 +15,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetDateTimeDeserTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetDateTimeDeserTest.java index 74f0102abf..71362dd509 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetDateTimeDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.*; import java.time.format.DateTimeFormatter; @@ -20,9 +20,9 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserTest.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserTest.java index 065c4bf2af..ea8cd44f23 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OffsetTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/OffsetTimeDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.OffsetTime; import java.time.ZoneOffset; @@ -17,8 +17,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java index 6be0d599f2..6c4f5a0ba8 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Month; import java.time.temporal.TemporalAccessor; @@ -16,9 +16,9 @@ import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/PeriodDeserTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/PeriodDeserTest.java index 60edd3a90b..c915148553 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/PeriodDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/PeriodDeserTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Period; import java.time.temporal.TemporalAmount; @@ -29,9 +29,9 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.type.LogicalType; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/YearDeserTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/YearDeserTest.java index 47bde297ba..13a3272860 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/YearDeserTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.Year; import java.time.temporal.Temporal; @@ -29,8 +29,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserTest.java index 2e415e4098..19023dd701 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/YearMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/YearMonthDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.io.IOException; import java.time.Month; @@ -17,7 +17,7 @@ import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneIdDeserTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneIdDeserTest.java index 4f06dfdbd1..3578189584 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneIdDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneIdDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.ZoneId; import java.util.Map; @@ -12,10 +12,10 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.type.LogicalType; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneOffsetDeserTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneOffsetDeserTest.java index dc5cfea85f..7fdd1421c9 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZoneOffsetDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZoneOffsetDeserTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.ZoneId; import java.time.ZoneOffset; @@ -30,9 +30,9 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import tools.jackson.databind.type.LogicalType; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java index 350172b761..63b8356475 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser; +package tools.jackson.databind.ext.javatime.deser; import java.time.*; import java.time.format.DateTimeFormatter; @@ -18,8 +18,8 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializerTest.java similarity index 95% rename from src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializerTest.java index 2ef98a7eb7..a397b376c2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/deser/key/ZonedDateTimeKeyDeserializerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/key/ZonedDateTimeKeyDeserializerTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.deser.key; +package tools.jackson.databind.ext.javatime.deser.key; import java.time.ZonedDateTime; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/DurationAsKeyTest.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/DurationAsKeyTest.java index dfa4e0e8af..b925e88653 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/DurationAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/DurationAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.Duration; import java.util.Collections; @@ -9,7 +9,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/InstantAsKeyTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/InstantAsKeyTest.java index 34fcefec9d..ebb9c85c40 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/InstantAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/InstantAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.Instant; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateAsKeyTest.java similarity index 89% rename from src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateAsKeyTest.java index fcaaac660a..2d062e1fab 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.LocalDate; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateTimeAsKeyTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateTimeAsKeyTest.java index a11b29430a..a6bc233b19 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalDateTimeAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -11,7 +11,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.deser.DeserializationProblemHandler; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalTimeAsKeyTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/LocalTimeAsKeyTest.java index 20b4145775..f3fe7c62b1 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/LocalTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/LocalTimeAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.LocalTime; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/MonthDayAsKeyTest.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/MonthDayAsKeyTest.java index 260ef5eca3..28f1d9b3ae 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/MonthDayAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/MonthDayAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.MonthDay; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/OffsetDateTimeAsKeyTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/OffsetDateTimeAsKeyTest.java index d7517428ed..42d03deadf 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/OffsetDateTimeAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.Instant; import java.time.OffsetDateTime; @@ -10,7 +10,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/OffsetTimeAsKeyTest.java similarity index 95% rename from src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/OffsetTimeAsKeyTest.java index 6e0fd2a4b0..381e2c5e91 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/OffsetTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/OffsetTimeAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.OffsetTime; import java.time.ZoneOffset; @@ -9,7 +9,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/PeriodAsKeyTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/PeriodAsKeyTest.java index cf72e6d349..22b9b64071 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/PeriodAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/PeriodAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.Period; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/YearAsKeyTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/YearAsKeyTest.java index f0d0416339..c14d5d222a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/YearAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/YearAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.Year; import java.util.Collections; @@ -10,7 +10,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.exc.InvalidFormatException; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/YearMonthAsKeyTest.java similarity index 89% rename from src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/YearMonthAsKeyTest.java index 2d677f6d18..03bee96d1a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/YearMonthAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/YearMonthAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.YearMonth; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/ZoneIdAsKeyTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/ZoneIdAsKeyTest.java index 54727f31c5..66e071d399 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneIdAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/ZoneIdAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.ZoneId; import java.util.Map; @@ -8,7 +8,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/ZoneOffsetAsKeyTest.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/ZoneOffsetAsKeyTest.java index e36f5c4b12..bf857040df 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZoneOffsetAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/ZoneOffsetAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.ZoneOffset; import java.util.Map; @@ -7,7 +7,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java b/src/test/java/tools/jackson/databind/ext/javatime/key/ZonedDateTimeAsKeyTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/key/ZonedDateTimeAsKeyTest.java index 6664345314..90912ac382 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/key/ZonedDateTimeAsKeyTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/key/ZonedDateTimeAsKeyTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.key; +package tools.jackson.databind.ext.javatime.key; import java.time.*; import java.util.Map; @@ -9,7 +9,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java b/src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeExceptionTest.java similarity index 85% rename from src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeExceptionTest.java index 9dd77654f0..e798ed137a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeExceptionTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeExceptionTest.java @@ -1,11 +1,11 @@ -package tools.jackson.databind.ext.datetime.misc; +package tools.jackson.databind.ext.javatime.misc; import java.time.DateTimeException; import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java b/src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeSchemasTest.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeSchemasTest.java index 200e52d8fd..57f85867e5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DateTimeSchemasTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/misc/DateTimeSchemasTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.misc; +package tools.jackson.databind.ext.javatime.misc; import java.time.LocalDate; import java.time.LocalTime; @@ -9,8 +9,8 @@ import tools.jackson.core.JsonParser; import tools.jackson.databind.*; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.jsonFormatVisitors.*; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java b/src/test/java/tools/jackson/databind/ext/javatime/misc/DeductionTypeSerialization296Test.java similarity index 95% rename from src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/misc/DeductionTypeSerialization296Test.java index 082570c43d..6e4a697105 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/DeductionTypeSerialization296Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/misc/DeductionTypeSerialization296Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.misc; +package tools.jackson.databind.ext.javatime.misc; import java.time.*; @@ -8,8 +8,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; - -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java b/src/test/java/tools/jackson/databind/ext/javatime/misc/JDKSerializabilityTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/misc/JDKSerializabilityTest.java index caa67da23a..162ef8014c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/JDKSerializabilityTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/misc/JDKSerializabilityTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.misc; +package tools.jackson.databind.ext.javatime.misc; import java.io.*; import java.time.Year; @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.*; +import tools.jackson.databind.ext.javatime.*; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java b/src/test/java/tools/jackson/databind/ext/javatime/misc/UnsupportedTypesTest.java similarity index 88% rename from src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/misc/UnsupportedTypesTest.java index 51aef6bf64..62364ec763 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/misc/UnsupportedTypesTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/misc/UnsupportedTypesTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.misc; +package tools.jackson.databind.ext.javatime.misc; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjusters; @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/DurationSerTest.java similarity index 98% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/DurationSerTest.java index a87359fadc..6aba0e70d0 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/DurationSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/DurationSerTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Duration; import java.time.temporal.TemporalAmount; @@ -11,8 +11,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/InstantSerTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/InstantSerTest.java index 7fb85dec33..ba80bb7eb3 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/InstantSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/InstantSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Instant; import java.time.format.DateTimeFormatter; @@ -25,9 +25,9 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerTest.java index 0902bc8d5c..642611fa6e 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDate; import java.time.Month; @@ -27,8 +27,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerTest.java index 54829be2f5..e3a84d2a1c 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalDateTimeSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDateTime; import java.time.Month; @@ -26,8 +26,8 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerTest.java similarity index 96% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerTest.java index 162824da18..c51f498780 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/LocalTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/LocalTimeSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalTime; import java.time.format.DateTimeFormatter; @@ -26,8 +26,9 @@ import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.annotation.JsonSerialize; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.ser.LocalTimeSerializer; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerTest.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerTest.java index 8871548b32..7a9053936a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/MonthDaySerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/MonthDaySerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Month; import java.time.MonthDay; @@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerTest.java index 8482e306eb..f644feea05 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetDateTimeSerTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.*; import java.time.format.DateTimeFormatter; @@ -11,9 +11,9 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerTest.java index daa5789300..5635fc36d9 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OffsetTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/OffsetTimeSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.OffsetTime; import java.time.ZoneOffset; @@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.*; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java index 7b7f422041..1ec5904a72 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Month; @@ -7,8 +7,8 @@ import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/PeriodSerTest.java similarity index 90% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/PeriodSerTest.java index 10a7279ad1..0ff889e53d 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/PeriodSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/PeriodSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Period; import java.time.temporal.TemporalAmount; @@ -22,8 +22,8 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateSerializationWithCustomFormatter.java similarity index 92% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateSerializationWithCustomFormatter.java index a34f1cc9b9..e0bd2f81df 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateSerializationWithCustomFormatter.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -9,9 +9,10 @@ import tools.jackson.core.json.JsonWriteFeature; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.LocalDateDeserializer; +import tools.jackson.databind.ext.javatime.ser.LocalDateSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.deser.LocalDateDeserializer; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java index bf840f8007..d95bf6bb00 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalDateTimeSerializationWithCustomFormatter.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -8,9 +8,10 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer; +import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.deser.LocalDateTimeDeserializer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalTimeSerializationWithCustomFormatter.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalTimeSerializationWithCustomFormatter.java index 557a9654d6..f0c94b7899 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestLocalTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestLocalTimeSerializationWithCustomFormatter.java @@ -1,13 +1,14 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.stream.Stream; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.LocalTimeDeserializer; +import tools.jackson.databind.ext.javatime.ser.LocalTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.deser.LocalTimeDeserializer; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearMonthSerializationWithCustomFormatter.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearMonthSerializationWithCustomFormatter.java index b936cd1ba3..57d3bc8fca 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearMonthSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearMonthSerializationWithCustomFormatter.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.YearMonth; import java.time.format.DateTimeFormatter; @@ -8,9 +8,10 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.YearMonthDeserializer; +import tools.jackson.databind.ext.javatime.ser.YearMonthSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.deser.YearMonthDeserializer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearSerializationWithCustomFormatter.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearSerializationWithCustomFormatter.java index 8733c1a8e5..2d6a13be46 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestYearSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestYearSerializationWithCustomFormatter.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Year; import java.time.format.DateTimeFormatter; @@ -8,9 +8,10 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ext.javatime.deser.YearDeserializer; +import tools.jackson.databind.ext.javatime.ser.YearSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.deser.YearDeserializer; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java index 34c8b40234..0309516db1 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/TestZonedDateTimeSerializationWithCustomFormatter.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -11,6 +11,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.ext.javatime.ser.ZonedDateTimeSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/WriteNanosecondsTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/WriteNanosecondsTest.java index 7b071df332..ab61716eb2 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteNanosecondsTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/WriteNanosecondsTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.*; @@ -8,7 +8,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/WriteZoneIdTest.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/WriteZoneIdTest.java index f12c8a14a2..e98b202a30 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/WriteZoneIdTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/WriteZoneIdTest.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Instant; import java.time.ZoneId; @@ -10,8 +10,8 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializationTest.java similarity index 97% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializationTest.java index d83c45cf96..d227fe8552 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearMonthSerializationTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/YearMonthSerializationTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Month; import java.time.YearMonth; @@ -27,8 +27,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/YearSerTest.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/YearSerTest.java index 6c82635b7c..f08f77872a 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/YearSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/YearSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.Year; import java.time.temporal.Temporal; @@ -24,8 +24,8 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerTest.java similarity index 90% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerTest.java index 6c741f4b5f..a4efe3c7ef 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneIdSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneIdSerTest.java @@ -14,15 +14,15 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZoneId; import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneOffsetSerTest.java similarity index 92% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneOffsetSerTest.java index 2ee26fea5b..beb40b0566 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZoneOffsetSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZoneOffsetSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZoneId; import java.time.ZoneOffset; @@ -22,8 +22,8 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java similarity index 99% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java index 8e7244a7aa..c6938cd34d 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java @@ -14,7 +14,7 @@ * limitations under the license. */ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.*; import java.time.format.DateTimeFormatter; @@ -35,9 +35,10 @@ import tools.jackson.databind.cfg.JavaTimeFeature; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ext.datetime.MockObjectConfiguration; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; -import tools.jackson.databind.ext.datetime.util.DecimalUtils; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.MockObjectConfiguration; +import tools.jackson.databind.ext.javatime.ser.ZonedDateTimeSerializer; +import tools.jackson.databind.ext.javatime.util.DecimalUtils; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerWithJsonFormat333Test.java similarity index 92% rename from src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerWithJsonFormat333Test.java index a3721a9e6e..436cc6fdc5 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/ser/ZonedDateTimeSerWithJsonFormat333Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerWithJsonFormat333Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.ser; +package tools.jackson.databind.ext.javatime.ser; import java.time.ZonedDateTime; @@ -8,7 +8,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java b/src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantDeserializerNegative359Test.java similarity index 91% rename from src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantDeserializerNegative359Test.java index 9c55c544ac..a3d7711d65 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantDeserializerNegative359Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantDeserializerNegative359Test.java @@ -1,11 +1,11 @@ -package tools.jackson.databind.ext.datetime.tofix; +package tools.jackson.databind.ext.javatime.tofix; import java.time.Instant; import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java b/src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantViaBigDecimal307Test.java similarity index 93% rename from src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantViaBigDecimal307Test.java index cd63f9349c..59fecdd11b 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/InstantViaBigDecimal307Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/tofix/InstantViaBigDecimal307Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.tofix; +package tools.jackson.databind.ext.javatime.tofix; import java.time.Instant; @@ -7,7 +7,7 @@ import tools.jackson.databind.JsonNode; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java b/src/test/java/tools/jackson/databind/ext/javatime/tofix/OffsetDateTimeDeser279Test.java similarity index 92% rename from src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/tofix/OffsetDateTimeDeser279Test.java index a37c240ba7..82a9c43261 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/OffsetDateTimeDeser279Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/tofix/OffsetDateTimeDeser279Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.tofix; +package tools.jackson.databind.ext.javatime.tofix; import java.time.OffsetDateTime; import java.time.ZoneId; @@ -9,7 +9,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java b/src/test/java/tools/jackson/databind/ext/javatime/tofix/ZonedDateTimeIssue244Test.java similarity index 94% rename from src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java rename to src/test/java/tools/jackson/databind/ext/javatime/tofix/ZonedDateTimeIssue244Test.java index dfc5721461..305518d256 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/tofix/ZonedDateTimeIssue244Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/tofix/ZonedDateTimeIssue244Test.java @@ -1,4 +1,4 @@ -package tools.jackson.databind.ext.datetime.tofix; +package tools.jackson.databind.ext.javatime.tofix; import java.time.ZoneId; import java.time.ZoneOffset; @@ -8,7 +8,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java b/src/test/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverterTest.java similarity index 88% rename from src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java rename to src/test/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverterTest.java index 7e254252ab..700a13cb56 100644 --- a/src/test/java/tools/jackson/databind/ext/datetime/util/DurationUnitConverterTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/util/DurationUnitConverterTest.java @@ -1,10 +1,11 @@ -package tools.jackson.databind.ext.datetime.util; +package tools.jackson.databind.ext.javatime.util; import java.time.temporal.ChronoUnit; import org.junit.jupiter.api.Test; -import tools.jackson.databind.ext.datetime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.DateTimeTestBase; +import tools.jackson.databind.ext.javatime.util.DurationUnitConverter; import static org.junit.jupiter.api.Assertions.*; From 765ca8315dc6ed4fc537536fdf5f8a4a1d02aeab Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 15:45:29 -0700 Subject: [PATCH 19/21] Minor javadoc improvements --- .../databind/ext/javatime/package-info.java | 7 +++++++ .../jackson/databind/ext/package-info.java | 20 +++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 src/main/java/tools/jackson/databind/ext/javatime/package-info.java diff --git a/src/main/java/tools/jackson/databind/ext/javatime/package-info.java b/src/main/java/tools/jackson/databind/ext/javatime/package-info.java new file mode 100644 index 0000000000..c83bff19ea --- /dev/null +++ b/src/main/java/tools/jackson/databind/ext/javatime/package-info.java @@ -0,0 +1,7 @@ +/** +Contains support for Java (8) Time (JSR-310) types: always available (as of Jackson 3.0) +but included similar to {@code JacksonModule}s for better configurability. + +*/ + +package tools.jackson.databind.ext.javatime; diff --git a/src/main/java/tools/jackson/databind/ext/package-info.java b/src/main/java/tools/jackson/databind/ext/package-info.java index aa8e25e823..bcafe9cbc5 100644 --- a/src/main/java/tools/jackson/databind/ext/package-info.java +++ b/src/main/java/tools/jackson/databind/ext/package-info.java @@ -3,21 +3,21 @@ may or may not be present in runtime environment, but that are commonly enough used so that explicit support can be added.

    -Currently supported extensions include: +Currently included extensions are:

      -
    • Support for Java 1.5 core XML datatypes: the reason these are -considered "external" is that some platforms that claim to be 1.5 conformant +
    • Java core XML datatypes: the reason these are +considered "external" is that some platforms that claim to be conformant are only partially so (Google Android, GAE) and do not included these - types. + types; and with Java 9 and above also due to JPMS reasons.
    • -
    • Joda time. This package has superior date/time handling functionality, -and is thus supported. However, to minimize forced dependencies this -support is added as extension so that Joda is not needed by Jackson -itself: but if it is present, its core types are supported to some -degree +
    • Selected {@code java.sql} types. +
    • +
    • Selected {@code java.beans} annotations: {@code @Transient}, {@code ConstructorProperties}. +
    • +
    • Java (8) Time (JSR-310) type support: as of Jackson 3.0 included in databind + but added similar to {@code JacksonModule}s for improved configurability.
    - */ package tools.jackson.databind.ext; From c5ffdd80da814464a83b95e9a49f0cd7349ba6b6 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 15:58:11 -0700 Subject: [PATCH 20/21] Rename JavaTimeFeature as DateTimeFeature for use with java.util/Joda date/time types --- .../jackson/databind/cfg/DatatypeFeatures.java | 2 +- ...vaTimeFeature.java => DateTimeFeature.java} | 13 ++++++++----- .../ext/javatime/JavaTimeInitializer.java | 4 ++-- .../javatime/deser/InstantDeserializer.java | 18 +++++++++--------- .../javatime/deser/LocalDateDeserializer.java | 10 +++++----- .../deser/LocalDateTimeDeserializer.java | 10 +++++----- .../javatime/deser/InstantDeser291Test.java | 4 ++-- .../ext/javatime/deser/LocalDateDeserTest.java | 4 ++-- .../javatime/deser/LocalDateTimeDeserTest.java | 4 ++-- .../javatime/deser/OneBasedMonthDeserTest.java | 10 +++++----- .../javatime/deser/ZonedDateTimeDeserTest.java | 4 ++-- .../ext/javatime/ser/OneBasedMonthSerTest.java | 6 +++--- .../ext/javatime/ser/ZonedDateTimeSerTest.java | 4 ++-- 13 files changed, 48 insertions(+), 45 deletions(-) rename src/main/java/tools/jackson/databind/cfg/{JavaTimeFeature.java => DateTimeFeature.java} (85%) diff --git a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java index 66785de8d7..f3c506c074 100644 --- a/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java +++ b/src/main/java/tools/jackson/databind/cfg/DatatypeFeatures.java @@ -339,7 +339,7 @@ private static class DefaultHolder DEFAULT_FEATURES = new DatatypeFeatures( collectDefaults(EnumFeature.values()), 0, collectDefaults(JsonNodeFeature.values()), 0, - collectDefaults(JavaTimeFeature.values()), 0 + collectDefaults(DateTimeFeature.values()), 0 ); } diff --git a/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java b/src/main/java/tools/jackson/databind/cfg/DateTimeFeature.java similarity index 85% rename from src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java rename to src/main/java/tools/jackson/databind/cfg/DateTimeFeature.java index b27588f63d..e8193db7e2 100644 --- a/src/main/java/tools/jackson/databind/cfg/JavaTimeFeature.java +++ b/src/main/java/tools/jackson/databind/cfg/DateTimeFeature.java @@ -1,11 +1,14 @@ package tools.jackson.databind.cfg; -import tools.jackson.databind.ext.javatime.JavaTimeInitializer; - /** - * Configurable on/off features for Java 8 Date/Time module ({@link JavaTimeInitializer}). + * Configurable on/off features to configure Date/Time handling. + * Mostly used to configure + * Java 8 Time ({@code java.time}) type handling (see + * {@link tools.jackson.databind.ext.javatime.JavaTimeInitializer}) + * but also to "legacy" ({@link java.util.Date}, {@link java.util.Calendar}) + * and Joda Date/Time. */ -public enum JavaTimeFeature implements DatatypeFeature +public enum DateTimeFeature implements DatatypeFeature { /** * Feature that determines whether {@link java.time.ZoneId} is normalized @@ -62,7 +65,7 @@ public enum JavaTimeFeature implements DatatypeFeature private final int _mask; - private JavaTimeFeature(boolean enabledByDefault) { + private DateTimeFeature(boolean enabledByDefault) { _enabledByDefault = enabledByDefault; _mask = (1 << ordinal()); } diff --git a/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java b/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java index 945826e3c9..5c84100aba 100644 --- a/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/JavaTimeInitializer.java @@ -20,7 +20,7 @@ import tools.jackson.databind.*; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.deser.ValueInstantiator; import tools.jackson.databind.deser.ValueInstantiators; import tools.jackson.databind.deser.std.StdValueInstantiator; @@ -158,7 +158,7 @@ public void setupModule(JacksonModule.SetupContext context) { ); // [modules-java8#274]: 1-based Month (de)serializer need to be applied via modifiers: - final boolean oneBasedMonthEnabled = context.isEnabled(JavaTimeFeature.ONE_BASED_MONTHS); + final boolean oneBasedMonthEnabled = context.isEnabled(DateTimeFeature.ONE_BASED_MONTHS); context.addDeserializerModifier(new JavaTimeDeserializerModifier(oneBasedMonthEnabled)); context.addSerializerModifier(new JavaTimeSerializerModifier(oneBasedMonthEnabled)); diff --git a/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java index b7c07b20b6..e06cdfdb4e 100644 --- a/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/InstantDeserializer.java @@ -35,7 +35,7 @@ import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.ext.javatime.util.DecimalUtils; /** @@ -47,9 +47,9 @@ public class InstantDeserializer extends JSR310DateTimeDeserializerBase { - private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault(); + private final static boolean DEFAULT_NORMALIZE_ZONE_ID = DateTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault(); private final static boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS - = JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault(); + = DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault(); /** * Constants used to check if ISO 8601 time string is colon-less. See [jackson-modules-java8#131] @@ -133,7 +133,7 @@ private static OffsetDateTime decimalToOffsetDateTime(FromDecimalArguments args) /** * Flag set from - * {@link JavaTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to + * {@link DateTimeFeature#NORMALIZE_DESERIALIZED_ZONE_ID} to * determine whether {@link ZoneId} is to be normalized during deserialization. * * @since 2.16 @@ -142,7 +142,7 @@ private static OffsetDateTime decimalToOffsetDateTime(FromDecimalArguments args) /** * Flag set from - * {@link JavaTimeFeature#ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS} + * {@link DateTimeFeature#ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS} * to determine whether stringified numbers are interpreted as timestamps * (enabled) nor not (disabled) in addition to a custom pattern ({code DateTimeFormatter}). *

    @@ -259,8 +259,8 @@ protected InstantDeserializer(InstantDeserializer base, _adjustToContextTZOverride = base._adjustToContextTZOverride; _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; - _normalizeZoneId = features.isEnabled(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID); - _alwaysAllowStringifiedDateTimestamps = features.isEnabled(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS); + _normalizeZoneId = features.isEnabled(DateTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID); + _alwaysAllowStringifiedDateTimestamps = features.isEnabled(DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS); } @Override @@ -277,8 +277,8 @@ protected InstantDeserializer withLeniency(Boolean leniency) { } public InstantDeserializer withFeatures(DatatypeFeatures features) { - if ((_normalizeZoneId == features.isEnabled(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) - && (_alwaysAllowStringifiedDateTimestamps == features.isEnabled(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) + if ((_normalizeZoneId == features.isEnabled(DateTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID)) + && (_alwaysAllowStringifiedDateTimestamps == features.isEnabled(DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS)) ) { return this; } diff --git a/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java index 8cb452000b..06a08d466b 100644 --- a/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java +++ b/src/main/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserializer.java @@ -31,7 +31,7 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; import tools.jackson.databind.cfg.DatatypeFeatures; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; /** * Deserializer for Java 8 temporal {@link LocalDate}s. @@ -41,7 +41,7 @@ public class LocalDateDeserializer extends JSR310DateTimeDeserializerBase { private final static boolean DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING - = JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); + = DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; @@ -49,7 +49,7 @@ public class LocalDateDeserializer extends JSR310DateTimeDeserializerBase { private final static boolean DEFAULT_USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING - = JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); + = DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING.enabledByDefault(); private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; @@ -53,7 +53,7 @@ public class LocalDateTimeDeserializer /** * Flag set from - * {@link JavaTimeFeature#USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING} + * {@link DateTimeFeature#USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING} * to determine whether the {@link java.util.TimeZone} of the * {@link tools.jackson.databind.DeserializationContext} is used * when leniently deserializing from the UTC/ISO instant format. @@ -100,7 +100,7 @@ protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, protected LocalDateTimeDeserializer(LocalDateTimeDeserializer base, DatatypeFeatures features) { super(LocalDateTime.class, base._formatter); _readTimestampsAsNanosOverride = base._readTimestampsAsNanosOverride; - _useTimeZoneForLenientDateParsing = features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); + _useTimeZoneForLenientDateParsing = features.isEnabled(DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING); } @Override @@ -133,7 +133,7 @@ protected JSR310DateTimeDeserializerBase _withFormatOverrides(Deserialization */ public LocalDateTimeDeserializer withFeatures(DatatypeFeatures features) { if (_useTimeZoneForLenientDateParsing == - features.isEnabled(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { + features.isEnabled(DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING)) { return this; } return new LocalDateTimeDeserializer(this, features); diff --git a/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java index a4c47cec5c..d9e3b49da9 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/InstantDeser291Test.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; @@ -19,7 +19,7 @@ public class InstantDeser291Test { private final JsonMapper MAPPER = JsonMapper.builder() .defaultLocale(Locale.ENGLISH) - .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) + .enable(DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build(); private final ObjectReader READER = MAPPER.readerFor(Instant.class); diff --git a/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java index b6a09cdaf3..dd7f902374 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateDeserTest.java @@ -16,7 +16,7 @@ import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.core.type.TypeReference; @@ -33,7 +33,7 @@ public class LocalDateDeserTest extends DateTimeTestBase private final ObjectMapper MAPPER = newMapper(); private final ObjectReader READER = MAPPER.readerFor(LocalDate.class); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) + .enable(DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDate.class); diff --git a/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java index 2c39dba1ba..ff1bd6659d 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/LocalDateTimeDeserTest.java @@ -35,7 +35,7 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.*; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.deser.DeserializationProblemHandler; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; @@ -57,7 +57,7 @@ public class LocalDateTimeDeserTest .build(); private final ObjectReader READER_USING_TIME_ZONE = JsonMapper.builder() - .enable(JavaTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) + .enable(DateTimeFeature.USE_TIME_ZONE_FOR_LENIENT_DATE_PARSING) .build() .readerFor(LocalDateTime.class); diff --git a/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java index 6c4f5a0ba8..c9dd95bb2b 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/OneBasedMonthDeserTest.java @@ -13,7 +13,7 @@ import tools.jackson.databind.ObjectReader; import tools.jackson.databind.cfg.CoercionAction; import tools.jackson.databind.cfg.CoercionInputShape; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.exc.InvalidFormatException; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.javatime.DateTimeTestBase; @@ -123,7 +123,7 @@ public void testDeserializationWithTypeInfo01_oneBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) - .enable(JavaTimeFeature.ONE_BASED_MONTHS) + .enable(DateTimeFeature.ONE_BASED_MONTHS) .build(); TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",11]", TemporalAccessor.class); @@ -135,7 +135,7 @@ public void testDeserializationWithTypeInfo01_zeroBased() throws Exception { ObjectMapper MAPPER = JsonMapper.builder() .addMixIn(TemporalAccessor.class, MockObjectConfiguration.class) - .disable(JavaTimeFeature.ONE_BASED_MONTHS) + .disable(DateTimeFeature.ONE_BASED_MONTHS) .build(); TemporalAccessor value = MAPPER.readValue("[\"java.time.Month\",\"11\"]", TemporalAccessor.class); @@ -193,14 +193,14 @@ public void testDeserializeFromEmptyString() throws Exception private ObjectReader readerForZeroBased() { return JsonMapper.builder() - .disable(JavaTimeFeature.ONE_BASED_MONTHS) + .disable(DateTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); } private ObjectReader readerForOneBased() { return JsonMapper.builder() - .enable(JavaTimeFeature.ONE_BASED_MONTHS) + .enable(DateTimeFeature.ONE_BASED_MONTHS) .build() .readerFor(Month.class); } diff --git a/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java index 63b8356475..a38124ddc0 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/deser/ZonedDateTimeDeserTest.java @@ -16,7 +16,7 @@ import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.exc.MismatchedInputException; import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; @@ -28,7 +28,7 @@ public class ZonedDateTimeDeserTest extends DateTimeTestBase private final ObjectReader READER = newMapper().readerFor(ZonedDateTime.class); private final ObjectReader READER_NON_NORMALIZED_ZONEID = JsonMapper.builder() - .disable(JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID) + .disable(DateTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID) .build() .readerFor(ZonedDateTime.class); diff --git a/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java index 1ec5904a72..8cb58a109b 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/OneBasedMonthSerTest.java @@ -6,7 +6,7 @@ import tools.jackson.databind.ObjectWriter; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.ext.javatime.DateTimeTestBase; import tools.jackson.databind.json.JsonMapper; @@ -49,14 +49,14 @@ public void testSerializationFromEnumWithPattern_zeroBased() throws Exception private ObjectWriter writerForZeroBased() { return JsonMapper.builder() - .disable(JavaTimeFeature.ONE_BASED_MONTHS) + .disable(DateTimeFeature.ONE_BASED_MONTHS) .build() .writer(); } private ObjectWriter writerForOneBased() { return JsonMapper.builder() - .enable(JavaTimeFeature.ONE_BASED_MONTHS) + .enable(DateTimeFeature.ONE_BASED_MONTHS) .build() .writer(); } diff --git a/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java index c6938cd34d..2a16e8f41c 100644 --- a/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java +++ b/src/test/java/tools/jackson/databind/ext/javatime/ser/ZonedDateTimeSerTest.java @@ -32,7 +32,7 @@ import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectReader; import tools.jackson.databind.SerializationFeature; -import tools.jackson.databind.cfg.JavaTimeFeature; +import tools.jackson.databind.cfg.DateTimeFeature; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.module.SimpleModule; import tools.jackson.databind.ext.javatime.DateTimeTestBase; @@ -912,7 +912,7 @@ public void testCustomPatternWithNumericTimestamp() throws Exception String input = a2q("{'value':'3.141592653'}"); Wrapper result = JsonMapper.builder() - .enable(JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) + .enable(DateTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS) .build() .readerFor(Wrapper.class) .readValue(input); From 53303b9db445cb0263a6097c4ae5587cba103943 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 4 Apr 2025 16:04:17 -0700 Subject: [PATCH 21/21] Minor reordering --- .../databind/SerializationFeature.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/tools/jackson/databind/SerializationFeature.java b/src/main/java/tools/jackson/databind/SerializationFeature.java index 15f1e86891..e070b5e45f 100644 --- a/src/main/java/tools/jackson/databind/SerializationFeature.java +++ b/src/main/java/tools/jackson/databind/SerializationFeature.java @@ -210,6 +210,21 @@ public enum SerializationFeature implements ConfigFeature */ WRITE_DATE_KEYS_AS_TIMESTAMPS(false), + /** + * Feature that controls whether numeric timestamp values are + * to be written using nanosecond timestamps (enabled) or not (disabled); + * if and only if datatype supports such resolution. + * Only newer datatypes (such as Java8 Date/Time) support such resolution -- + * older types (pre-Java8 java.util.Date etc) and Joda do not -- + * and this setting has no effect on such types. + *

    + * If disabled, standard millisecond timestamps are assumed. + * This is the counterpart to {@link DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS}. + *

    + * Feature is enabled by default, to support most accurate time values possible. + */ + WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true), + /** * Feature that determines whether date/date-time values should be serialized * so that they include timezone id, in cases where type itself contains @@ -360,21 +375,6 @@ public enum SerializationFeature implements ConfigFeature */ WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false), - /** - * Feature that controls whether numeric timestamp values are - * to be written using nanosecond timestamps (enabled) or not (disabled); - * if and only if datatype supports such resolution. - * Only newer datatypes (such as Java8 Date/Time) support such resolution -- - * older types (pre-Java8 java.util.Date etc) and Joda do not -- - * and this setting has no effect on such types. - *

    - * If disabled, standard millisecond timestamps are assumed. - * This is the counterpart to {@link DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS}. - *

    - * Feature is enabled by default, to support most accurate time values possible. - */ - WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true), - /** * Feature that determines whether {@link java.util.Map} entries are first * sorted by key before serialization or not: if enabled, additional sorting