Skip to content

Commit d14fc9a

Browse files
committed
Merge branch '2.11'
2 parents 7ebd3a0 + f81820d commit d14fc9a

24 files changed

+788
-54
lines changed

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/DurationDeserializer.java

+39-1
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,30 @@
1616

1717
package com.fasterxml.jackson.datatype.jsr310.deser;
1818

19+
import com.fasterxml.jackson.annotation.JsonFormat;
1920
import com.fasterxml.jackson.core.JsonParser;
2021
import com.fasterxml.jackson.core.JsonToken;
2122
import com.fasterxml.jackson.core.JsonTokenId;
23+
import com.fasterxml.jackson.databind.BeanProperty;
2224
import com.fasterxml.jackson.databind.DeserializationContext;
2325
import com.fasterxml.jackson.databind.DeserializationFeature;
26+
import com.fasterxml.jackson.databind.JsonDeserializer;
27+
import com.fasterxml.jackson.databind.JsonMappingException;
28+
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
2429
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
2530

2631
import java.io.IOException;
2732
import java.math.BigDecimal;
2833
import java.time.DateTimeException;
2934
import java.time.Duration;
3035

36+
3137
/**
3238
* Deserializer for Java 8 temporal {@link Duration}s.
3339
*
3440
* @author Nick Williams
3541
*/
36-
public class DurationDeserializer extends JSR310DeserializerBase<Duration>
42+
public class DurationDeserializer extends JSR310DeserializerBase<Duration> implements ContextualDeserializer
3743
{
3844
public static final DurationDeserializer INSTANCE = new DurationDeserializer();
3945

@@ -42,6 +48,18 @@ private DurationDeserializer()
4248
super(Duration.class);
4349
}
4450

51+
/**
52+
* Since 2.11
53+
*/
54+
protected DurationDeserializer(DurationDeserializer base, Boolean leniency) {
55+
super(base, leniency);
56+
}
57+
58+
@Override
59+
protected DurationDeserializer withLeniency(Boolean leniency) {
60+
return new DurationDeserializer(this, leniency);
61+
}
62+
4563
@Override
4664
public Duration deserialize(JsonParser parser, DeserializationContext context) throws IOException
4765
{
@@ -60,6 +78,9 @@ public Duration deserialize(JsonParser parser, DeserializationContext context) t
6078
case JsonTokenId.ID_STRING:
6179
String string = parser.getText().trim();
6280
if (string.length() == 0) {
81+
if (!isLenient()) {
82+
return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
83+
}
6384
return null;
6485
}
6586
try {
@@ -78,4 +99,21 @@ public Duration deserialize(JsonParser parser, DeserializationContext context) t
7899
return _handleUnexpectedToken(context, parser, JsonToken.VALUE_STRING,
79100
JsonToken.VALUE_NUMBER_INT, JsonToken.VALUE_NUMBER_FLOAT);
80101
}
102+
103+
@Override
104+
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
105+
BeanProperty property) throws JsonMappingException
106+
{
107+
JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
108+
DurationDeserializer deser = this;
109+
if (format != null) {
110+
if (format.hasLenient()) {
111+
Boolean leniency = format.getLenient();
112+
if (leniency != null) {
113+
deser = deser.withLeniency(leniency);
114+
}
115+
}
116+
}
117+
return deser;
118+
}
81119
}

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/InstantDeserializer.java

+27-6
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ public class InstantDeserializer<T extends Temporal>
101101

102102
/**
103103
* Flag for <code>JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE</code>
104-
*
105-
* @since 2.8
106104
*/
107105
protected final Boolean _adjustToContextTZOverride;
108106

@@ -147,6 +145,18 @@ protected InstantDeserializer(InstantDeserializer<T> base, Boolean adjustToConte
147145
_adjustToContextTZOverride = adjustToContextTimezoneOverride;
148146
}
149147

148+
@SuppressWarnings("unchecked")
149+
protected InstantDeserializer(InstantDeserializer<T> base, DateTimeFormatter f, Boolean leniency)
150+
{
151+
super((Class<T>) base.handledType(), f, leniency);
152+
parsedToValue = base.parsedToValue;
153+
fromMilliseconds = base.fromMilliseconds;
154+
fromNanoseconds = base.fromNanoseconds;
155+
adjust = base.adjust;
156+
replaceZeroOffsetAsZ = (_formatter == DateTimeFormatter.ISO_INSTANT);
157+
_adjustToContextTZOverride = base._adjustToContextTZOverride;
158+
}
159+
150160
@Override
151161
protected InstantDeserializer<T> withDateFormat(DateTimeFormatter dtf) {
152162
if (dtf == _formatter) {
@@ -155,10 +165,12 @@ protected InstantDeserializer<T> withDateFormat(DateTimeFormatter dtf) {
155165
return new InstantDeserializer<T>(this, dtf);
156166
}
157167

158-
// !!! TODO: lenient vs strict?
159168
@Override
160169
protected InstantDeserializer<T> withLeniency(Boolean leniency) {
161-
return this;
170+
if (_isLenient == !Boolean.FALSE.equals(leniency)) {
171+
return this;
172+
}
173+
return new InstantDeserializer<T>(this, _formatter, leniency);
162174
}
163175

164176
@Override
@@ -182,6 +194,9 @@ public T deserialize(JsonParser parser, DeserializationContext context) throws I
182194
{
183195
String string = parser.getText().trim();
184196
if (string.length() == 0) {
197+
if (!isLenient()) {
198+
return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
199+
}
185200
return null;
186201
}
187202
// only check for other parsing modes if we are using default formatter
@@ -241,10 +256,16 @@ public JsonDeserializer<T> createContextual(DeserializationContext ctxt,
241256
if (deserializer != this) {
242257
JsonFormat.Value val = findFormatOverrides(ctxt, property, handledType());
243258
if (val != null) {
244-
return new InstantDeserializer<>(deserializer, val.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE));
259+
deserializer = new InstantDeserializer<>(deserializer, val.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE));
260+
if (val.hasLenient()) {
261+
Boolean leniency = val.getLenient();
262+
if (leniency != null) {
263+
deserializer = deserializer.withLeniency(leniency);
264+
}
265+
}
245266
}
246267
}
247-
return this;
268+
return deserializer;
248269
}
249270

250271
protected boolean shouldAdjustToContextTimezone(DeserializationContext context) {

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DateTimeDeserializerBase.java

+13-9
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public abstract class JSR310DateTimeDeserializerBase<T>
3333
* and has to be explicitly change to force strict handling: this is to keep backwards
3434
* compatibility with earlier versions.
3535
*/
36-
protected final boolean _isLenient;
36+
// protected final boolean _isLenient;
3737

3838
/**
3939
* Setting that indicates the {@Link JsonFormat.Shape} specified for this deserializer
@@ -43,31 +43,36 @@ public abstract class JSR310DateTimeDeserializerBase<T>
4343
* NUMBER_INT, and the deserializer was not specified with the leniency setting of true,
4444
* then an exception will be thrown.
4545
* @see [jackson-modules-java8#58] for more info
46-
*
47-
* @since 2.11
4846
*/
4947
protected final Shape _shape;
5048

5149
protected JSR310DateTimeDeserializerBase(Class<T> supportedType, DateTimeFormatter f) {
5250
super(supportedType);
5351
_formatter = f;
54-
_isLenient = true;
52+
// _isLenient = true;
53+
_shape = null;
54+
}
55+
56+
public JSR310DateTimeDeserializerBase(Class<T> supportedType, DateTimeFormatter f, Boolean leniency) {
57+
super(supportedType, leniency);
58+
_formatter = f;
59+
// _isLenient = !Boolean.FALSE.equals(leniency);
5560
_shape = null;
5661
}
5762

5863
protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase<T> base,
5964
DateTimeFormatter f) {
6065
super(base);
6166
_formatter = f;
62-
_isLenient = base._isLenient;
67+
// _isLenient = base._isLenient;
6368
_shape = base._shape;
6469
}
6570

6671
protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase<T> base,
6772
Boolean leniency) {
68-
super(base);
73+
super(base, leniency);
6974
_formatter = base._formatter;
70-
_isLenient = !Boolean.FALSE.equals(leniency);
75+
// _isLenient = !Boolean.FALSE.equals(leniency);
7176
_shape = base._shape;
7277
}
7378

@@ -79,10 +84,9 @@ protected JSR310DateTimeDeserializerBase(JSR310DateTimeDeserializerBase<T> base,
7984
super(base);
8085
_formatter = base._formatter;
8186
_shape = shape;
82-
_isLenient = base._isLenient;
87+
// _isLenient = base._isLenient;
8388
}
8489

85-
8690
protected abstract JSR310DateTimeDeserializerBase<T> withDateFormat(DateTimeFormatter dtf);
8791
protected abstract JSR310DateTimeDeserializerBase<T> withLeniency(Boolean leniency);
8892

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310DeserializerBase.java

+52-1
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,49 @@
3737
*/
3838
abstract class JSR310DeserializerBase<T> extends StdScalarDeserializer<T>
3939
{
40+
/**
41+
* Flag that indicates what leniency setting is enabled for this deserializer (either
42+
* due {@link JsonFormat} annotation on property or class, or due to per-type
43+
* "config override", or from global settings): leniency/strictness has effect
44+
* on accepting some non-default input value representations (such as integer values
45+
* for dates).
46+
*<p>
47+
* Note that global default setting is for leniency to be enabled, for Jackson 2.x,
48+
* and has to be explicitly change to force strict handling: this is to keep backwards
49+
* compatibility with earlier versions.
50+
*
51+
* @since 2.11
52+
*/
53+
protected final boolean _isLenient;
54+
4055
protected JSR310DeserializerBase(Class<T> supportedType) {
4156
super(supportedType);
57+
_isLenient = true;
4258
}
4359

44-
protected JSR310DeserializerBase(JSR310DeserializerBase<?> base) {
60+
protected JSR310DeserializerBase(Class<T> supportedType,
61+
Boolean leniency) {
62+
super(supportedType);
63+
_isLenient = !Boolean.FALSE.equals(leniency);
64+
}
65+
66+
protected JSR310DeserializerBase(JSR310DeserializerBase<T> base) {
4567
super(base);
68+
_isLenient = base._isLenient;
69+
}
70+
71+
protected JSR310DeserializerBase(JSR310DeserializerBase<T> base, Boolean leniency) {
72+
super(base);
73+
_isLenient = !Boolean.FALSE.equals(leniency);
74+
}
75+
76+
protected abstract JSR310DeserializerBase<T> withLeniency(Boolean leniency);
77+
78+
/**
79+
* @return {@code true} if lenient handling is enabled; {code false} if not (strict mode)
80+
*/
81+
protected boolean isLenient() {
82+
return _isLenient;
4683
}
4784

4885
@Override
@@ -136,6 +173,20 @@ protected <R> R _handleUnexpectedToken(DeserializationContext context,
136173
ClassUtil.getClassDescription(handledType()));
137174
}
138175

176+
@SuppressWarnings("unchecked")
177+
protected T _failForNotLenient(JsonParser p, DeserializationContext ctxt,
178+
JsonToken expToken) throws IOException
179+
{
180+
return (T) ctxt.handleUnexpectedToken(getValueType(ctxt), expToken, p,
181+
"Cannot deserialize instance of %s out of %s token: not allowed because 'strict' mode set for property or type (enable 'lenient' handling to allow)",
182+
ClassUtil.nameOf(handledType()), p.currentToken());
183+
}
184+
185+
/*
186+
public Object handleUnexpectedToken(Class<?> instClass, JsonToken t,
187+
JsonParser p, String msg, Object... msgArgs)
188+
*/
189+
139190
/**
140191
* Helper method used to peel off spurious wrappings of DateTimeException
141192
*

datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/deser/JSR310StringParsableDeserializer.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@
2222
import java.time.ZoneId;
2323
import java.time.ZoneOffset;
2424

25+
import com.fasterxml.jackson.annotation.JsonFormat;
2526
import com.fasterxml.jackson.core.JsonParser;
2627

2728
import com.fasterxml.jackson.core.JsonToken;
2829

30+
import com.fasterxml.jackson.databind.BeanProperty;
2931
import com.fasterxml.jackson.databind.DeserializationContext;
3032
import com.fasterxml.jackson.databind.JsonDeserializer;
33+
import com.fasterxml.jackson.databind.JsonMappingException;
3134
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
3235

3336
/**
@@ -63,18 +66,39 @@ protected JSR310StringParsableDeserializer(Class<?> supportedType, int type)
6366
_typeSelector = type;
6467
}
6568

69+
/**
70+
* Since 2.11
71+
*/
72+
protected JSR310StringParsableDeserializer(JSR310StringParsableDeserializer base, Boolean leniency) {
73+
super(base, leniency);
74+
_typeSelector = base._typeSelector;
75+
}
76+
6677
@SuppressWarnings("unchecked")
6778
protected static <T> JsonDeserializer<T> createDeserializer(Class<T> type, int typeId) {
6879
return (JsonDeserializer<T>) new JSR310StringParsableDeserializer(type, typeId);
6980
}
7081

82+
@Override
83+
protected JSR310StringParsableDeserializer withLeniency(Boolean leniency) {
84+
if (_isLenient == !Boolean.FALSE.equals(leniency)) {
85+
return this;
86+
}
87+
// TODO: or should this be casting as above in createDeserializer? But then in createContext, we need to
88+
// call the withLeniency method in this class. (See if we can follow InstantDeser convention here?)
89+
return new JSR310StringParsableDeserializer(this, leniency);
90+
}
91+
7192
@Override
7293
public Object deserialize(JsonParser parser, DeserializationContext context) throws IOException
7394
{
7495
if (parser.hasToken(JsonToken.VALUE_STRING)) {
7596
String string = parser.getText().trim();
7697
if (string.isEmpty()) {
77-
return _coerceEmptyString(context, false);
98+
if (!isLenient()) {
99+
return _failForNotLenient(parser, context, JsonToken.VALUE_STRING);
100+
}
101+
return null;
78102
}
79103
try {
80104
switch (_typeSelector) {
@@ -114,4 +138,21 @@ public Object deserializeWithType(JsonParser parser, DeserializationContext cont
114138
}
115139
return deserializer.deserializeTypedFromAny(parser, context);
116140
}
141+
142+
@Override
143+
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
144+
BeanProperty property) throws JsonMappingException
145+
{
146+
JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
147+
JSR310StringParsableDeserializer deser = this;
148+
if (format != null) {
149+
if (format.hasLenient()) {
150+
Boolean leniency = format.getLenient();
151+
if (leniency != null) {
152+
deser = this.withLeniency(leniency);
153+
}
154+
}
155+
}
156+
return deser;
157+
}
117158
}

0 commit comments

Comments
 (0)