16
16
17
17
package com .fasterxml .jackson .datatype .jsr310 .deser ;
18
18
19
+ import java .io .IOException ;
20
+ import java .math .BigDecimal ;
21
+ import java .time .DateTimeException ;
22
+ import java .time .Duration ;
23
+ import java .time .temporal .ChronoUnit ;
24
+ import java .time .temporal .TemporalUnit ;
25
+ import java .util .*;
26
+ import java .util .stream .Collectors ;
27
+
19
28
import com .fasterxml .jackson .annotation .JsonFormat ;
29
+
20
30
import com .fasterxml .jackson .core .JsonParser ;
21
31
import com .fasterxml .jackson .core .JsonToken ;
22
32
import com .fasterxml .jackson .core .JsonTokenId ;
23
33
import com .fasterxml .jackson .core .StreamReadCapability ;
24
34
import com .fasterxml .jackson .core .io .NumberInput ;
25
- import com .fasterxml .jackson .databind .BeanProperty ;
26
- import com .fasterxml .jackson .databind .DeserializationContext ;
27
- import com .fasterxml .jackson .databind .DeserializationFeature ;
28
- import com .fasterxml .jackson .databind .JsonDeserializer ;
29
- import com .fasterxml .jackson .databind .JsonMappingException ;
35
+
36
+ import com .fasterxml .jackson .databind .*;
30
37
import com .fasterxml .jackson .databind .deser .ContextualDeserializer ;
31
38
import com .fasterxml .jackson .datatype .jsr310 .DecimalUtils ;
32
39
33
- import java .io .IOException ;
34
- import java .math .BigDecimal ;
35
- import java .time .DateTimeException ;
36
- import java .time .Duration ;
37
- import java .time .temporal .ChronoUnit ;
38
- import java .time .temporal .TemporalUnit ;
39
- import java .util .Arrays ;
40
- import java .util .Collections ;
41
- import java .util .HashSet ;
42
- import java .util .Optional ;
43
- import java .util .Set ;
44
-
45
-
46
40
/**
47
41
* Deserializer for Java 8 temporal {@link Duration}s.
48
42
*
49
43
* @author Nick Williams
50
- * @since 2.2.0
44
+ * @since 2.2
51
45
*/
52
46
public class DurationDeserializer extends JSR310DeserializerBase <Duration >
53
47
implements ContextualDeserializer
@@ -57,39 +51,52 @@ public class DurationDeserializer extends JSR310DeserializerBase<Duration>
57
51
public static final DurationDeserializer INSTANCE = new DurationDeserializer ();
58
52
59
53
/**
60
- * Since 2.12
61
- * When set, integer values will be deserialized using the specified unit. Using this parser will tipically
62
- * override the value specified in {@link DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS} as it is
63
- * considered that the unit set in {@link JsonFormat#pattern()} has precedence since is more specific.
54
+ * When defined (not {@code null}) integer values will be converted into duration
55
+ * unit configured for the converter.
56
+ * Using this converter will typically override the value specified in
57
+ * {@link DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS} as it is
58
+ * considered that the unit set in {@link JsonFormat#pattern()} has precedence
59
+ * since it is more specific.
60
+ *<p>
61
+ * See [jackson-modules-java8#184] for more info.
64
62
*
65
- * @see [jackson-modules-java8#184] for more info
63
+ * @since 2.12
66
64
*/
67
- private DurationUnitParser _durationUnitParser ;
65
+ protected final DurationUnitConverter _durationUnitConverter ;
68
66
69
- private DurationDeserializer () {
67
+ public DurationDeserializer () {
70
68
super (Duration .class );
69
+ _durationUnitConverter = null ;
71
70
}
72
71
73
72
/**
74
- * Since 2.11
73
+ * @since 2.11
75
74
*/
76
75
protected DurationDeserializer (DurationDeserializer base , Boolean leniency ) {
77
76
super (base , leniency );
77
+ _durationUnitConverter = base ._durationUnitConverter ;
78
78
}
79
79
80
- protected DurationDeserializer (DurationDeserializer base , DurationUnitParser durationUnitParser ) {
80
+ /**
81
+ * @since 2.12
82
+ */
83
+ protected DurationDeserializer (DurationDeserializer base , DurationUnitConverter converter ) {
81
84
super (base , base ._isLenient );
82
- _durationUnitParser = durationUnitParser ;
85
+ _durationUnitConverter = converter ;
83
86
}
84
87
85
88
@ Override
86
89
protected DurationDeserializer withLeniency (Boolean leniency ) {
87
90
return new DurationDeserializer (this , leniency );
88
91
}
89
92
93
+ protected DurationDeserializer withConverter (DurationUnitConverter pattern ) {
94
+ return new DurationDeserializer (this , pattern );
95
+ }
96
+
90
97
@ Override
91
98
public JsonDeserializer <?> createContextual (DeserializationContext ctxt ,
92
- BeanProperty property ) throws JsonMappingException
99
+ BeanProperty property ) throws JsonMappingException
93
100
{
94
101
JsonFormat .Value format = findFormatOverrides (ctxt , property , handledType ());
95
102
DurationDeserializer deser = this ;
@@ -101,18 +108,20 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
101
108
}
102
109
}
103
110
if (format .hasPattern ()) {
104
- deser = DurationUnitParser .from (format .getPattern ())
105
- .map (deser ::withPattern )
106
- .orElse (deser );
111
+ final String pattern = format .getPattern ();
112
+ DurationUnitConverter p = DurationUnitConverter .from (pattern );
113
+ if (p == null ) {
114
+ ctxt .reportBadDefinition (getValueType (ctxt ),
115
+ String .format (
116
+ "Bad 'pattern' definition (\" %s\" ) for `Duration`: expected one of [%s]" ,
117
+ pattern , DurationUnitConverter .descForAllowed ()));
118
+ }
119
+ deser = deser .withConverter (p );
107
120
}
108
121
}
109
122
return deser ;
110
123
}
111
124
112
- private DurationDeserializer withPattern (DurationUnitParser pattern ) {
113
- return new DurationDeserializer (this , pattern );
114
- }
115
-
116
125
@ Override
117
126
public Duration deserialize (JsonParser parser , DeserializationContext context ) throws IOException
118
127
{
@@ -122,11 +131,7 @@ public Duration deserialize(JsonParser parser, DeserializationContext context) t
122
131
BigDecimal value = parser .getDecimalValue ();
123
132
return DecimalUtils .extractSecondsAndNanos (value , Duration ::ofSeconds );
124
133
case JsonTokenId .ID_NUMBER_INT :
125
- long intValue = parser .getLongValue ();
126
- if (_durationUnitParser != null ) {
127
- return _durationUnitParser .parse (intValue );
128
- }
129
- return _fromTimestamp (context , intValue );
134
+ return _fromTimestamp (context , parser .getLongValue ());
130
135
case JsonTokenId .ID_STRING :
131
136
return _fromString (parser , context , parser .getText ());
132
137
// 30-Sep-2020, tatu: New! "Scalar from Object" (mostly for XML)
@@ -170,14 +175,22 @@ && _isValidTimestampString(value)) {
170
175
}
171
176
172
177
protected Duration _fromTimestamp (DeserializationContext ctxt , long ts ) {
178
+ if (_durationUnitConverter != null ) {
179
+ return _durationUnitConverter .convert (ts );
180
+ }
181
+ // 20-Oct-2020, tatu: This makes absolutely no sense but... somehow
182
+ // became the default handling.
173
183
if (ctxt .isEnabled (DeserializationFeature .READ_DATE_TIMESTAMPS_AS_NANOSECONDS )) {
174
184
return Duration .ofSeconds (ts );
175
185
}
176
186
return Duration .ofMillis (ts );
177
187
}
178
188
179
- protected static class DurationUnitParser {
180
- final static Set <ChronoUnit > PARSEABLE_UNITS = Collections .unmodifiableSet (new HashSet <>(Arrays .asList (
189
+ protected static class DurationUnitConverter {
190
+ private final static Map <String , ChronoUnit > PARSEABLE_UNITS ;
191
+ static {
192
+ Map <String , ChronoUnit > units = new LinkedHashMap <>();
193
+ for (ChronoUnit unit : new ChronoUnit [] {
181
194
ChronoUnit .NANOS ,
182
195
ChronoUnit .MICROS ,
183
196
ChronoUnit .MILLIS ,
@@ -186,22 +199,35 @@ protected static class DurationUnitParser {
186
199
ChronoUnit .HOURS ,
187
200
ChronoUnit .HALF_DAYS ,
188
201
ChronoUnit .DAYS
189
- )));
202
+ }) {
203
+ units .put (unit .name (), unit );
204
+ }
205
+ PARSEABLE_UNITS = units ;
206
+ }
207
+
190
208
final TemporalUnit unit ;
191
209
192
- DurationUnitParser (TemporalUnit unit ) {
210
+ DurationUnitConverter (TemporalUnit unit ) {
193
211
this .unit = unit ;
194
212
}
195
213
196
- Duration parse (long value ) {
214
+ public Duration convert (long value ) {
197
215
return Duration .of (value , unit );
198
216
}
199
217
200
- static Optional <DurationUnitParser > from (String unit ) {
201
- return PARSEABLE_UNITS .stream ()
202
- .filter (u -> u .name ().equals (unit ))
203
- .map (DurationUnitParser ::new )
204
- .findFirst ();
218
+ /**
219
+ * @return Description of all allowed valued as a sequence of
220
+ * double-quoted values separated by comma
221
+ */
222
+ public static String descForAllowed () {
223
+ return "\" " + PARSEABLE_UNITS .keySet ().stream ()
224
+ .collect (Collectors .joining ("\" , \" " ))
225
+ +"\" " ;
226
+ }
227
+
228
+ static DurationUnitConverter from (String unit ) {
229
+ ChronoUnit chr = PARSEABLE_UNITS .get (unit );
230
+ return (chr == null ) ? null : new DurationUnitConverter (chr );
205
231
}
206
232
}
207
233
}
0 commit comments