Skip to content

Commit dd34fb6

Browse files
authored
Move Enum-related DeserializationFeatures into EnumFeature (#5088)
1 parent ceab973 commit dd34fb6

26 files changed

+203
-156
lines changed

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
1616
- Branch rename "master" -> "3.x" [JSTEP-12]
1717
#5080: Move Enum-related SerializationFeatures into EnumFeature (3.0)
1818
(contributed by Joo-Hyuk K)
19+
#5079: Move Enum-related DeserializationFeatures into EnumFeature (3.0)
20+
(contributed by Joo-Hyuk K)
1921

2022
3.0.0-rc3 (not yet released)
2123

src/main/java/tools/jackson/databind/DeserializationFeature.java

-56
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,6 @@ public enum DeserializationFeature implements ConfigFeature
129129
*/
130130
FAIL_ON_NULL_FOR_PRIMITIVES(true),
131131

132-
/**
133-
* Feature that determines whether JSON integer numbers are valid
134-
* values to be used for deserializing Java enum values.
135-
* If set to 'false' numbers are acceptable and are used to map to
136-
* ordinal() of matching enumeration value; if 'true', numbers are
137-
* not allowed and a {@link DatabindException} will be thrown.
138-
* Latter behavior makes sense if there is concern that accidental
139-
* mapping from integer values to enums might happen (and when enums
140-
* are always serialized as JSON Strings)
141-
*<p>
142-
* Feature is disabled by default.
143-
*/
144-
FAIL_ON_NUMBERS_FOR_ENUMS(false),
145-
146132
/**
147133
* Feature that determines what happens when type of a polymorphic
148134
* value (indicated for example by {@link com.fasterxml.jackson.annotation.JsonTypeInfo})
@@ -407,48 +393,6 @@ public enum DeserializationFeature implements ConfigFeature
407393
*/
408394
ACCEPT_FLOAT_AS_INT(true),
409395

410-
/**
411-
* Feature that determines the deserialization mechanism used for
412-
* Enum values: if enabled, Enums are assumed to have been serialized using
413-
* return value of {@code Enum.toString()};
414-
* if disabled, return value of {@code Enum.name()} is assumed to have been used.
415-
*<p>
416-
* Note: this feature should usually have same value
417-
* as {@link SerializationFeature#WRITE_ENUMS_USING_TO_STRING}.
418-
*<p>
419-
* Feature is enabled by default as of Jackson 3.0 (in 2.x it was disabled).
420-
*/
421-
READ_ENUMS_USING_TO_STRING(true),
422-
423-
/**
424-
* Feature that allows unknown Enum values to be parsed as {@code null} values.
425-
* If disabled, unknown Enum values will throw exceptions.
426-
* <p>
427-
* Note that in some cases this will effectively ignore unknown {@code Enum} values,
428-
* e.g. when the unknown values are used as keys of {@link java.util.EnumMap}
429-
* or values of {@link java.util.EnumSet}: this is because these data structures cannot
430-
* store {@code null} values.
431-
* <p>
432-
* Also note that this feature has lower precedence than
433-
* {@link DeserializationFeature#READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE},
434-
* meaning this feature will work only if latter feature is disabled.
435-
* <p>
436-
* Feature is disabled by default.
437-
*/
438-
READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
439-
440-
/**
441-
* Feature that allows unknown Enum values to be ignored and replaced by a predefined value specified through
442-
* {@link com.fasterxml.jackson.annotation.JsonEnumDefaultValue @JsonEnumDefaultValue} annotation.
443-
* If disabled, unknown Enum values will throw exceptions.
444-
* If enabled, but no predefined default Enum value is specified, an exception will be thrown as well.
445-
* <p>
446-
* Note that this feature has higher precedence than {@link DeserializationFeature#READ_UNKNOWN_ENUM_VALUES_AS_NULL}.
447-
* <p>
448-
* Feature is disabled by default.
449-
*/
450-
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),
451-
452396
/*
453397
/**********************************************************************
454398
/* Other

src/main/java/tools/jackson/databind/ObjectMapper.java

+11
Original file line numberDiff line numberDiff line change
@@ -2141,6 +2141,17 @@ public ObjectReader reader(DeserializationFeature first,
21412141
return _newReader(deserializationConfig().with(first, other));
21422142
}
21432143

2144+
/**
2145+
* Factory method for constructing {@link ObjectReader} with
2146+
* specified feature enabled (compared to settings that this
2147+
* mapper instance has).
2148+
* Note that the resulting instance is NOT usable as is,
2149+
* without defining expected value type.
2150+
*/
2151+
public ObjectReader reader(DatatypeFeature feature) {
2152+
return _newReader(deserializationConfig().with(feature));
2153+
}
2154+
21442155
/**
21452156
* Factory method for constructing {@link ObjectReader} that will
21462157
* update given Object (usually Bean, but can be a Collection or Map

src/main/java/tools/jackson/databind/cfg/CoercionConfigs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public CoercionAction findCoercion(DeserializationConfig config,
208208
break;
209209
case Integer:
210210
if (targetType == LogicalType.Enum) {
211-
if (config.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
211+
if (config.isEnabled(EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
212212
return CoercionAction.Fail;
213213
}
214214
}

src/main/java/tools/jackson/databind/cfg/EnumFeature.java

+83-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package tools.jackson.databind.cfg;
22

3-
import tools.jackson.databind.DeserializationFeature;
3+
import tools.jackson.databind.DatabindException;
44

55
/**
66
* New Datatype-specific configuration options related to handling of
77
* {@link java.lang.Enum} types.
88
*/
99
public enum EnumFeature implements DatatypeFeature
1010
{
11+
/*
12+
/**********************************************************************
13+
/* READ
14+
/**********************************************************************
15+
*/
16+
1117
/**
1218
* Feature that determines standard deserialization mechanism used for
1319
* Enum values: if enabled, Enums are assumed to have been serialized using
@@ -22,6 +28,81 @@ public enum EnumFeature implements DatatypeFeature
2228
*/
2329
READ_ENUM_KEYS_USING_INDEX(false),
2430

31+
/**
32+
* Feature that determines whether JSON integer numbers are valid
33+
* values to be used for deserializing Java enum values.
34+
* If set to 'false' numbers are acceptable and are used to map to
35+
* ordinal() of matching enumeration value; if 'true', numbers are
36+
* not allowed and a {@link DatabindException} will be thrown.
37+
* Latter behavior makes sense if there is concern that accidental
38+
* mapping from integer values to enums might happen (and when enums
39+
* are always serialized as JSON Strings)
40+
*<p>
41+
* Feature used to be one of {@link tools.jackson.databind.DeserializationFeature}s
42+
* in Jackson 2.x but was moved here in 3.0.
43+
*<p>
44+
* Feature is disabled by default.
45+
*/
46+
FAIL_ON_NUMBERS_FOR_ENUMS(false),
47+
48+
/**
49+
* Feature that determines the deserialization mechanism used for
50+
* Enum values: if enabled, Enums are assumed to have been serialized using
51+
* return value of {@code Enum.toString()};
52+
* if disabled, return value of {@code Enum.name()} is assumed to have been used.
53+
*<p>
54+
* Note: this feature should usually have same value
55+
* as {@link #WRITE_ENUMS_USING_TO_STRING}.
56+
*<p>
57+
* Feature used to be one of {@link tools.jackson.databind.DeserializationFeature}s
58+
* in Jackson 2.x but was moved here in 3.0.
59+
*<p>
60+
* Feature is enabled by default as of Jackson 3.0 (in 2.x it was disabled).
61+
*/
62+
READ_ENUMS_USING_TO_STRING(true),
63+
64+
/**
65+
* Feature that allows unknown Enum values to be parsed as {@code null} values.
66+
* If disabled, unknown Enum values will throw exceptions.
67+
* <p>
68+
* Note that in some cases this will effectively ignore unknown {@code Enum} values,
69+
* e.g. when the unknown values are used as keys of {@link java.util.EnumMap}
70+
* or values of {@link java.util.EnumSet}: this is because these data structures cannot
71+
* store {@code null} values.
72+
* <p>
73+
* Also note that this feature has lower precedence than
74+
* {@link #READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE},
75+
* meaning this feature will work only if latter feature is disabled.
76+
*<p>
77+
* Feature used to be one of {@link tools.jackson.databind.DeserializationFeature}s
78+
* in Jackson 2.x but was moved here in 3.0.
79+
*<p>
80+
* Feature is disabled by default.
81+
*/
82+
READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
83+
84+
/**
85+
* Feature that allows unknown Enum values to be ignored and replaced by a predefined value specified through
86+
* {@link com.fasterxml.jackson.annotation.JsonEnumDefaultValue @JsonEnumDefaultValue} annotation.
87+
* If disabled, unknown Enum values will throw exceptions.
88+
* If enabled, but no predefined default Enum value is specified, an exception will be thrown as well.
89+
* <p>
90+
* Note that this feature has higher precedence than {@link #READ_UNKNOWN_ENUM_VALUES_AS_NULL}.
91+
*<p>
92+
* Feature used to be one of {@link tools.jackson.databind.DeserializationFeature}s
93+
* in Jackson 2.x but was moved here in 3.0.
94+
*<p>
95+
* Feature is disabled by default.
96+
*/
97+
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),
98+
99+
/*
100+
/**********************************************************************
101+
/* WRITE
102+
/**********************************************************************
103+
*/
104+
105+
25106
/**
26107
* Feature that determines standard serialization mechanism used for
27108
* Enum values: if enabled, return value of <code>Enum.name().toLowerCase()</code>
@@ -43,7 +124,7 @@ public enum EnumFeature implements DatatypeFeature
43124
* is used; if disabled, return value of <code>Enum.name()</code> is used.
44125
*<p>
45126
* Note: this feature should usually have same value
46-
* as {@link DeserializationFeature#READ_ENUMS_USING_TO_STRING}.
127+
* as {@link #READ_ENUMS_USING_TO_STRING}.
47128
*<p>
48129
* Feature used to be one of {@link tools.jackson.databind.SerializationFeature}s
49130
* in Jackson 2.x but was moved here in 3.0.

src/main/java/tools/jackson/databind/cfg/MapperBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ public B configureForJackson2() {
814814
.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,
815815
DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
816816
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
817-
.disable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
817+
.disable(EnumFeature.READ_ENUMS_USING_TO_STRING)
818818
.enable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
819819
.enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS,
820820
DateTimeFeature.WRITE_DURATIONS_AS_TIMESTAMPS)

src/main/java/tools/jackson/databind/deser/jdk/EnumDeserializer.java

+7-9
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
import tools.jackson.core.*;
99
import tools.jackson.databind.*;
1010
import tools.jackson.databind.annotation.JacksonStdImpl;
11-
import tools.jackson.databind.cfg.CoercionAction;
12-
import tools.jackson.databind.cfg.CoercionInputShape;
13-
import tools.jackson.databind.cfg.MapperConfig;
11+
import tools.jackson.databind.cfg.*;
1412
import tools.jackson.databind.deser.SettableBeanProperty;
1513
import tools.jackson.databind.deser.ValueInstantiator;
1614
import tools.jackson.databind.deser.std.StdScalarDeserializer;
@@ -222,7 +220,7 @@ private CompactStringObjectMap _resolveCurrentLookup(DeserializationContext ctxt
222220
if (_lookupByEnumNaming != null) {
223221
return _lookupByEnumNaming;
224222
}
225-
return ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
223+
return ctxt.isEnabled(EnumFeature.READ_ENUMS_USING_TO_STRING)
226224
? _lookupByToString
227225
: _lookupByName;
228226
}
@@ -236,9 +234,9 @@ protected Object _fromInteger(JsonParser p, DeserializationContext ctxt,
236234

237235
// First, check legacy setting for slightly different message
238236
if (act == CoercionAction.Fail) {
239-
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
237+
if (ctxt.isEnabled(EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
240238
return ctxt.handleWeirdNumberValue(_enumClass(), index,
241-
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
239+
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
242240
);
243241
}
244242
// otherwise this will force failure with new setting
@@ -318,7 +316,7 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext
318316
return match;
319317
}
320318
}
321-
if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
319+
if (!ctxt.isEnabled(EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
322320
&& !_isFromIntValue) {
323321
// [databind#149]: Allow use of 'String' indexes as well -- unless prohibited (as per above)
324322
char c = name.charAt(0);
@@ -375,7 +373,7 @@ protected boolean useNullForUnknownEnum(DeserializationContext ctxt) {
375373
if (_useNullForUnknownEnum != null) {
376374
return _useNullForUnknownEnum;
377375
}
378-
return ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
376+
return ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
379377
}
380378

381379
// @since 2.15
@@ -387,7 +385,7 @@ protected boolean useDefaultValueForUnknownEnum(DeserializationContext ctxt) {
387385
return _useDefaultValueForUnknownEnum;
388386
}
389387
// Otherwise, check the global setting
390-
return ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
388+
return ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
391389
}
392390
// No default value? then false
393391
return false;

src/main/java/tools/jackson/databind/deser/jdk/EnumMapDeserializer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import tools.jackson.core.*;
66
import tools.jackson.databind.*;
7+
import tools.jackson.databind.cfg.EnumFeature;
78
import tools.jackson.databind.deser.*;
89
import tools.jackson.databind.deser.bean.PropertyBasedCreator;
910
import tools.jackson.databind.deser.bean.PropertyValueBuffer;
@@ -267,7 +268,7 @@ public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt,
267268
Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyStr, ctxt);
268269
JsonToken t = p.nextToken();
269270
if (key == null) {
270-
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
271+
if (!ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
271272
return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyStr,
272273
"value not one of declared Enum instance names for %s",
273274
_containerType.getKeyType());
@@ -360,7 +361,7 @@ public EnumMap<?,?> _deserializeUsingProperties(JsonParser p, DeserializationCon
360361
// but we need to let key deserializer handle it separately, nonetheless
361362
Enum<?> key = (Enum<?>) _keyDeserializer.deserializeKey(keyName, ctxt);
362363
if (key == null) {
363-
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
364+
if (!ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
364365
return (EnumMap<?,?>) ctxt.handleWeirdStringValue(_enumClass, keyName,
365366
"value not one of declared Enum instance names for %s",
366367
_containerType.getKeyType());

src/main/java/tools/jackson/databind/deser/jdk/FactoryBasedEnumDeserializer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tools.jackson.core.JsonToken;
66

77
import tools.jackson.databind.*;
8+
import tools.jackson.databind.cfg.EnumFeature;
89
import tools.jackson.databind.deser.SettableBeanProperty;
910
import tools.jackson.databind.deser.ValueInstantiator;
1011
import tools.jackson.databind.deser.bean.PropertyBasedCreator;
@@ -194,14 +195,14 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt)
194195
Throwable t = ClassUtil.throwRootCauseIfJacksonE(e);
195196
if (t instanceof IllegalArgumentException) {
196197
// [databind#4979]: unknown as default
197-
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
198+
if (ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
198199
// ... only if we DO have a default
199200
if (_defaultValue != null) {
200201
return _defaultValue;
201202
}
202203
}
203204
// [databind#1642]: unknown as null
204-
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
205+
if (ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
205206
return null;
206207
}
207208
// 12-Oct-2021, tatu: Should probably try to provide better exception since

src/main/java/tools/jackson/databind/deser/jdk/JDKKeyDeserializer.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public Object deserializeKey(String key, DeserializationContext ctxt)
142142
ClassUtil.exceptionMessage(re));
143143
}
144144
if (ClassUtil.isEnumType(_keyClass)
145-
&& ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
145+
&& ctxt.getConfig().isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
146146
return null;
147147
}
148148
return ctxt.handleWeirdKey(_keyClass, key, "not a valid representation");
@@ -418,9 +418,9 @@ public Object _parse(String key, DeserializationContext ctxt)
418418
}
419419
if (e == null) {
420420
if ((_enumDefaultValue != null)
421-
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
421+
&& ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
422422
e = _enumDefaultValue;
423-
} else if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
423+
} else if (!ctxt.isEnabled(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
424424
return ctxt.handleWeirdKey(_keyClass, key, "not one of the values accepted for Enum class: %s",
425425
res.getEnumIds());
426426
}
@@ -436,7 +436,7 @@ protected EnumResolver _resolveCurrentResolver(DeserializationContext ctxt) {
436436
if (_byEnumNamingResolver != null) {
437437
return _byEnumNamingResolver;
438438
}
439-
return ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
439+
return ctxt.isEnabled(EnumFeature.READ_ENUMS_USING_TO_STRING)
440440
? _byToStringResolver
441441
: _byNameResolver;
442442
}

src/test/java/tools/jackson/databind/convert/CoerceEnumTest.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
66

77
import tools.jackson.databind.*;
8-
import tools.jackson.databind.cfg.CoercionAction;
9-
import tools.jackson.databind.cfg.CoercionInputShape;
8+
import tools.jackson.databind.cfg.*;
109
import tools.jackson.databind.exc.MismatchedInputException;
1110
import tools.jackson.databind.type.LogicalType;
1211

@@ -43,7 +42,7 @@ protected enum EnumCoerce {
4342
public void testLegacyDefaults() throws Exception
4443
{
4544
// first, verify default settings which do not accept empty String:
46-
assertFalse(MAPPER.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
45+
assertFalse(MAPPER.isEnabled(EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
4746
}
4847

4948
@Test
@@ -98,7 +97,7 @@ public void testEnumFromIntFailLegacy() throws Exception
9897
assertEquals(EnumCoerce.values()[1], result);
9998

10099
try {
101-
r.with(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
100+
r.with(EnumFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
102101
.readValue("1");
103102
fail("Should not pass");
104103
} catch (Exception e) {

0 commit comments

Comments
 (0)