1 /*
2  * Copyright 2013 FasterXML.com
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may
5  * not use this file except in compliance with the License. You may obtain
6  * a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the license for the specific language governing permissions and
14  * limitations under the license.
15  */

16
17 package com.fasterxml.jackson.datatype.jsr310;
18
19 import java.time.*;
20
21 import com.fasterxml.jackson.databind.BeanDescription;
22 import com.fasterxml.jackson.databind.DeserializationConfig;
23 import com.fasterxml.jackson.databind.JavaType;
24 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
25 import com.fasterxml.jackson.databind.deser.ValueInstantiators;
26 import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
27 import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
28 import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver;
29 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
30 import com.fasterxml.jackson.databind.module.SimpleModule;
31 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
32 import com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer;
33 import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
34 import com.fasterxml.jackson.datatype.jsr310.deser.JSR310StringParsableDeserializer;
35 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
36 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
37 import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
38 import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer;
39 import com.fasterxml.jackson.datatype.jsr310.deser.OffsetTimeDeserializer;
40 import com.fasterxml.jackson.datatype.jsr310.deser.YearDeserializer;
41 import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer;
42 import com.fasterxml.jackson.datatype.jsr310.deser.key.*;
43 import com.fasterxml.jackson.datatype.jsr310.ser.DurationSerializer;
44 import com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer;
45 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
46 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
47 import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
48 import com.fasterxml.jackson.datatype.jsr310.ser.MonthDaySerializer;
49 import com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer;
50 import com.fasterxml.jackson.datatype.jsr310.ser.OffsetTimeSerializer;
51 import com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer;
52 import com.fasterxml.jackson.datatype.jsr310.ser.YearSerializer;
53 import com.fasterxml.jackson.datatype.jsr310.ser.ZoneIdSerializer;
54 import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer;
55 import com.fasterxml.jackson.datatype.jsr310.ser.key.ZonedDateTimeKeySerializer;
56
57 /**
58  * Class that registers capability of serializing {@code java.time} objects with the Jackson core.
59  *
60  * <pre>
61  * ObjectMapper mapper = new ObjectMapper();
62  * mapper.registerModule(new JavaTimeModule());
63  * </pre>
64  *<p>
65  * Note that as of 2.x, if auto-registering modules, this package will register
66  * legacy version, {@link JSR310Module}, and NOT this module. 3.x will change the default.
67  * Legacy version has the same functionality, but slightly different default configuration:
68  * see {@link com.fasterxml.jackson.datatype.jsr310.JSR310Module} for details.
69  *<p>
70  * Most {@code java.time} types are serialized as numbers (integers or decimals as appropriate) if the
71  * {@link com.fasterxml.jackson.databind.SerializationFeature#WRITE_DATES_AS_TIMESTAMPS} feature is enabled
72  * (or, for {@link Duration}, {@link com.fasterxml.jackson.databind.SerializationFeature#WRITE_DURATIONS_AS_TIMESTAMPS}),
73  * and otherwise are serialized in standard
74  * <a href="http://en.wikipedia.org/wiki/ISO_8601" target="_blank">ISO-8601</a> string representation.
75  * ISO-8601 specifies formats for representing offset dates and times, zoned dates and times,
76  * local dates and times, periods, durations, zones, and more. All {@code java.time} types
77  * have built-in translation to and from ISO-8601 formats.
78  * <p>
79  * Granularity of timestamps is controlled through the companion features
80  * {@link com.fasterxml.jackson.databind.SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} and
81  * {@link com.fasterxml.jackson.databind.DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS}. For serialization, timestamps are
82  * written as fractional numbers (decimals), where the number is seconds and the decimal is fractional seconds, if
83  * {@code WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), with resolution as fine as nanoseconds depending on the
84  * underlying JDK implementation. If {@code WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS} is disabled, timestamps are written as a whole number of
85  * milliseconds. At deserialization time, decimal numbers are always read as fractional second timestamps with up-to-nanosecond resolution,
86  * since the meaning of the decimal is unambiguous. The more ambiguous integer types are read as fractional seconds without a decimal point
87  * if {@code READ_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), and otherwise they are read as milliseconds.
88  * <p>
89  * Some exceptions to this standard serialization/deserialization rule:
90  * <ul>
91  * <li>{@link Period}, which always results in an ISO-8601 format because Periods must be represented in years, months, and/or days.</li>
92  * <li>{@link Year}, which only contains a year and cannot be represented with a timestamp.</li>
93  * <li>{@link YearMonth}, which only contains a year and a month and cannot be represented with a timestamp.</li>
94  * <li>{@link MonthDay}, which only contains a month and a day and cannot be represented with a timestamp.</li>
95  * <li>{@link ZoneId} and {@link ZoneOffset}, which do not actually store dates and times but are supported with this module nonetheless.</li>
96  * <li>{@link LocalDate}, {@link LocalTime}, {@link LocalDateTime}, and {@link OffsetTime}, which cannot portably be converted to timestamps
97  * and are instead represented as arrays when WRITE_DATES_AS_TIMESTAMPS is enabled.</li>
98  * </ul>
99  *
100  * @author Nick Williams
101  * @author Zoltan Kiss
102  *
103  * @since 2.6
104  *
105  * @see com.fasterxml.jackson.datatype.jsr310.ser.key.Jsr310NullKeySerializer
106  */

107 @SuppressWarnings("javadoc")
108 public final class JavaTimeModule extends SimpleModule
109 {
110     private static final long serialVersionUID = 1L;
111
112     public JavaTimeModule()
113     {
114         super(PackageVersion.VERSION);
115
116         // First deserializers
117
118         // // Instant variants:
119         addDeserializer(Instant.class, InstantDeserializer.INSTANT);
120         addDeserializer(OffsetDateTime.class, InstantDeserializer.OFFSET_DATE_TIME);
121         addDeserializer(ZonedDateTime.class, InstantDeserializer.ZONED_DATE_TIME);
122
123         // // Other deserializers
124         addDeserializer(Duration.class, DurationDeserializer.INSTANCE);
125         addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
126         addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
127         addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
128         addDeserializer(MonthDay.class, MonthDayDeserializer.INSTANCE);
129         addDeserializer(OffsetTime.class, OffsetTimeDeserializer.INSTANCE);
130         addDeserializer(Period.class, JSR310StringParsableDeserializer.PERIOD);
131         addDeserializer(Year.class, YearDeserializer.INSTANCE);
132         addDeserializer(YearMonth.class, YearMonthDeserializer.INSTANCE);
133         addDeserializer(ZoneId.class, JSR310StringParsableDeserializer.ZONE_ID);
134         addDeserializer(ZoneOffset.class, JSR310StringParsableDeserializer.ZONE_OFFSET);
135
136         // then serializers:
137         addSerializer(Duration.class, DurationSerializer.INSTANCE);
138         addSerializer(Instant.class, InstantSerializer.INSTANCE);
139         addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
140         addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);
141         addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
142         addSerializer(MonthDay.class, MonthDaySerializer.INSTANCE);
143         addSerializer(OffsetDateTime.class, OffsetDateTimeSerializer.INSTANCE);
144         addSerializer(OffsetTime.class, OffsetTimeSerializer.INSTANCE);
145         addSerializer(Period.classnew ToStringSerializer(Period.class));
146         addSerializer(Year.class, YearSerializer.INSTANCE);
147         addSerializer(YearMonth.class, YearMonthSerializer.INSTANCE);
148
149         /* 27-Jun-2015, tatu: This is the real difference from the old
150          *  {@link JSR310Module}: default is to produce ISO-8601 compatible
151          *  serialization with timezone offset only, not timezone id.
152          *  But this is configurable.
153          */

154         addSerializer(ZonedDateTime.class, ZonedDateTimeSerializer.INSTANCE);
155         
156         // since 2.11: need to override Type Id handling
157         // (actual concrete type is `ZoneRegion`, but that's not visible)
158         addSerializer(ZoneId.classnew ZoneIdSerializer());
159         addSerializer(ZoneOffset.classnew ToStringSerializer(ZoneOffset.class));
160
161         // key serializers
162         addKeySerializer(ZonedDateTime.class, ZonedDateTimeKeySerializer.INSTANCE);
163
164         // key deserializers
165         addKeyDeserializer(Duration.class, DurationKeyDeserializer.INSTANCE);
166         addKeyDeserializer(Instant.class, InstantKeyDeserializer.INSTANCE);
167         addKeyDeserializer(LocalDateTime.class, LocalDateTimeKeyDeserializer.INSTANCE);
168         addKeyDeserializer(LocalDate.class, LocalDateKeyDeserializer.INSTANCE);
169         addKeyDeserializer(LocalTime.class, LocalTimeKeyDeserializer.INSTANCE);
170         addKeyDeserializer(MonthDay.class, MonthDayKeyDeserializer.INSTANCE);
171         addKeyDeserializer(OffsetDateTime.class, OffsetDateTimeKeyDeserializer.INSTANCE);
172         addKeyDeserializer(OffsetTime.class, OffsetTimeKeyDeserializer.INSTANCE);
173         addKeyDeserializer(Period.class, PeriodKeyDeserializer.INSTANCE);
174         addKeyDeserializer(Year.class, YearKeyDeserializer.INSTANCE);
175         addKeyDeserializer(YearMonth.class, YearMonthKeyDeserializer.INSTANCE);
176         addKeyDeserializer(ZonedDateTime.class, ZonedDateTimeKeyDeserializer.INSTANCE);
177         addKeyDeserializer(ZoneId.class, ZoneIdKeyDeserializer.INSTANCE);
178         addKeyDeserializer(ZoneOffset.class, ZoneOffsetKeyDeserializer.INSTANCE);
179     }
180
181     @Override
182     public void setupModule(SetupContext context) {
183         super.setupModule(context);
184         context.addValueInstantiators(new ValueInstantiators.Base() {
185             @Override
186             public ValueInstantiator findValueInstantiator(DeserializationConfig config,
187                     BeanDescription beanDesc, ValueInstantiator defaultInstantiator)
188             {
189                 JavaType type = beanDesc.getType();
190                 Class<?> raw = type.getRawClass();
191
192                 // 15-May-2015, tatu: In theory not safe, but in practice we do need to do "fuzzy" matching
193                 // because we will (for now) be getting a subtype, but in future may want to downgrade
194                 // to the common base type. Even more, serializer may purposefully force use of base type.
195                 // So... in practice it really should always work, in the end. :)
196                 if (ZoneId.class.isAssignableFrom(raw)) {
197                     // let's assume we should be getting "empty" StdValueInstantiator here:
198                     if (defaultInstantiator instanceof StdValueInstantiator) {
199                         StdValueInstantiator inst = (StdValueInstantiator) defaultInstantiator;
200                         // one further complication: we need ZoneId info, not sub-class
201                         AnnotatedClass ac;
202                         if (raw == ZoneId.class) {
203                             ac = beanDesc.getClassInfo();
204                         } else {
205                             // we don't need Annotations, so constructing directly is fine here
206                             // even if it's not generally recommended
207                             ac = AnnotatedClassResolver.resolve(config,
208                                     config.constructType(ZoneId.class), config);
209                         }
210                         if (!inst.canCreateFromString()) {
211                             AnnotatedMethod factory = _findFactory(ac, "of", String.class);
212                             if (factory != null) {
213                                 inst.configureFromStringCreator(factory);
214                             }
215                             // otherwise... should we indicate an error?
216                         }
217                         // return ZoneIdInstantiator.construct(config, beanDesc, defaultInstantiator);
218                     }
219                 }
220                 return defaultInstantiator;
221             }
222         });
223     }
224
225     protected AnnotatedMethod _findFactory(AnnotatedClass cls, String name, Class<?>... argTypes)
226     {
227         final int argCount = argTypes.length;
228         for (AnnotatedMethod method : cls.getFactoryMethods()) {
229             if (!name.equals(method.getName())
230                     || (method.getParameterCount() != argCount)) {
231                 continue;
232             }
233             for (int i = 0; i < argCount; ++i) {
234                 Class<?> argType = method.getParameter(i).getRawType();
235                 if (!argType.isAssignableFrom(argTypes[i])) {
236                     continue;
237                 }
238             }
239             return method;
240         }
241         return null;
242     }
243 }
244