Skip to content

Commit bd7adfb

Browse files
authored
Allow default enums with @JsonCreator (#4979)
1 parent 6743f4c commit bd7adfb

File tree

7 files changed

+72
-21
lines changed

7 files changed

+72
-21
lines changed

release-notes/CREDITS-2.x

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,3 +1918,7 @@ Floris Westerman (@FWest98)
19181918
Joren Inghelbrecht (@jin-harmoney)
19191919
* Contributed #4953: Allow clearing all caches to avoid classloader leaks
19201920
(2.19.0)
1921+
1922+
Will Paul (@dropofwill)
1923+
* Contributed #4979: Allow default enums with `@JsonCreator`
1924+
(2.19.0)

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ Project: jackson-databind
6666
(fix by Joo-Hyuk K)
6767
#4963: Serializing `Map.Entry` as Bean with `@JsonFormat.shape = Shape.OBJECT`
6868
fails on JDK 17+
69+
#4979: Allow default enums with `@JsonCreator`
70+
(contributed by Will P)
6971
#4997: `ObjectNode` put methods should do null check for key
7072
#5006: Add `MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_OPTIONALS` to prevent
7173
failure of `java.util.Optional` (de)serialization without Java 8 module

src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,9 @@ public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
10871087
"Invalid `@JsonCreator` annotated Enum factory method [%s]: needs to return compatible type",
10881088
factory.toString()));
10891089
}
1090-
deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory, valueInstantiator, creatorProps);
1090+
deser = EnumDeserializer.deserializerForCreator(
1091+
config, enumClass, factory, valueInstantiator, creatorProps,
1092+
constructEnumResolver(enumClass, config, beanDesc));
10911093
break;
10921094
}
10931095
}

src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -147,20 +147,20 @@ protected EnumDeserializer(EnumDeserializer base, Boolean caseInsensitive) {
147147
}
148148

149149
/**
150-
* @deprecated Since 2.9
151-
*/
152-
@Deprecated
153-
public EnumDeserializer(EnumResolver byNameResolver) {
154-
this(byNameResolver, null);
155-
}
156-
157-
/**
158-
* @deprecated Since 2.8
150+
* Factory method used when Enum instances are to be deserialized
151+
* using a creator (static factory method)
152+
*
153+
* @return Deserializer based on given factory method
154+
*
155+
* @since 2.8
156+
* @deprecated Since 2.19
159157
*/
160158
@Deprecated
161159
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
162-
Class<?> enumClass, AnnotatedMethod factory) {
163-
return deserializerForCreator(config, enumClass, factory, null, null);
160+
Class<?> enumClass, AnnotatedMethod factory,
161+
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps) {
162+
return deserializerForCreator(config, enumClass, factory, valueInstantiator,
163+
creatorProps, null);
164164
}
165165

166166
/**
@@ -169,19 +169,19 @@ public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig c
169169
*
170170
* @return Deserializer based on given factory method
171171
*
172-
* @since 2.8
172+
* @since 2.19
173173
*/
174174
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
175175
Class<?> enumClass, AnnotatedMethod factory,
176-
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
176+
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps,
177+
EnumResolver byNameResolver)
177178
{
178179
if (config.canOverrideAccessModifiers()) {
179180
ClassUtil.checkAndFixAccess(factory.getMember(),
180181
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
181182
}
182-
return new FactoryBasedEnumDeserializer(enumClass, factory,
183-
factory.getParameterType(0),
184-
valueInstantiator, creatorProps);
183+
return new FactoryBasedEnumDeserializer(enumClass, factory, factory.getParameterType(0),
184+
valueInstantiator, creatorProps, byNameResolver);
185185
}
186186

187187
/**

src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1616
import com.fasterxml.jackson.databind.type.LogicalType;
1717
import com.fasterxml.jackson.databind.util.ClassUtil;
18+
import com.fasterxml.jackson.databind.util.EnumResolver;
1819

1920
/**
2021
* Deserializer that uses a single-String static factory method
@@ -35,6 +36,9 @@ class FactoryBasedEnumDeserializer
3536
protected final ValueInstantiator _valueInstantiator;
3637
protected final SettableBeanProperty[] _creatorProps;
3738

39+
// @since 2.19
40+
protected final Enum<?> _defaultValue;
41+
3842
protected final boolean _hasArgs;
3943

4044
/**
@@ -44,8 +48,23 @@ class FactoryBasedEnumDeserializer
4448
*/
4549
private transient volatile PropertyBasedCreator _propCreator;
4650

51+
/**
52+
* @since 2.8
53+
* @deprecated since 2.19
54+
*/
55+
@Deprecated
4756
public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType paramType,
4857
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
58+
{
59+
this(cls, f, paramType, valueInstantiator, creatorProps, null);
60+
}
61+
62+
/**
63+
* @since 2.19
64+
*/
65+
public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType paramType,
66+
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps,
67+
EnumResolver enumResolver)
4968
{
5069
super(cls);
5170
_factory = f;
@@ -56,6 +75,7 @@ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType pa
5675
_deser = null;
5776
_valueInstantiator = valueInstantiator;
5877
_creatorProps = creatorProps;
78+
_defaultValue = (enumResolver == null) ? null : enumResolver.getDefaultValue();
5979
}
6080

6181
/**
@@ -70,6 +90,7 @@ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f)
7090
_deser = null;
7191
_valueInstantiator = null;
7292
_creatorProps = null;
93+
_defaultValue = null;
7394
}
7495

7596
protected FactoryBasedEnumDeserializer(FactoryBasedEnumDeserializer base,
@@ -80,6 +101,7 @@ protected FactoryBasedEnumDeserializer(FactoryBasedEnumDeserializer base,
80101
_hasArgs = base._hasArgs;
81102
_valueInstantiator = base._valueInstantiator;
82103
_creatorProps = base._creatorProps;
104+
_defaultValue = base._defaultValue;
83105

84106
_deser = deser;
85107
}
@@ -198,7 +220,14 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
198220
} catch (Exception e) {
199221
Throwable t = ClassUtil.throwRootCauseIfIOE(e);
200222
if (t instanceof IllegalArgumentException) {
201-
// [databind#1642]:
223+
// [databind#4979]: unknown as default
224+
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
225+
// ... only if we DO have a default
226+
if (_defaultValue != null) {
227+
return _defaultValue;
228+
}
229+
}
230+
// [databind#1642]: unknown as null
202231
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
203232
return null;
204233
}

src/test/java/com/fasterxml/jackson/databind/deser/creators/EnumCreatorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ public JsonDeserializer<?> findEnumDeserializer(final Class<?> type, final Deser
125125
for (AnnotatedMethod am : factoryMethods) {
126126
final JsonCreator creator = am.getAnnotation(JsonCreator.class);
127127
if (creator != null) {
128-
return EnumDeserializer.deserializerForCreator(config, type, am, null, null);
128+
return EnumDeserializer.deserializerForCreator(
129+
config, type, am, null, null, null);
129130
}
130131
}
131132
}

src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumDeserializationTest.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ static enum EnumWithDefaultAnnoAndConstructor {
161161
}
162162

163163
static enum StrictEnumCreator {
164-
A, B;
164+
A, B, @JsonEnumDefaultValue UNKNOWN;
165165

166166
@JsonCreator public static StrictEnumCreator fromId(String value) {
167167
for (StrictEnumCreator e: values()) {
@@ -453,7 +453,7 @@ public void testAllowUnknownEnumValuesReadAsNull() throws Exception
453453
assertNull(reader.forType(TestEnum.class).readValue(" 4343 "));
454454
}
455455

456-
// Ability to ignore unknown Enum values:
456+
// Ability to ignore unknown Enum values as null:
457457

458458
// [databind#1642]
459459
@Test
@@ -483,6 +483,19 @@ public void testAllowUnknownEnumValuesAsMapKeysReadAsNull() throws Exception
483483
assertTrue(result.map.containsKey(null));
484484
}
485485

486+
// Ability to ignore unknown Enum values as a defined default:
487+
488+
// [databind#4979]
489+
@Test
490+
public void testAllowUnknownEnumValuesReadAsDefaultWithCreatorMethod4979() throws Exception
491+
{
492+
ObjectReader reader = MAPPER.reader(
493+
DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
494+
assertEquals(
495+
StrictEnumCreator.UNKNOWN,
496+
reader.forType(StrictEnumCreator.class).readValue("\"NO-SUCH-VALUE\""));
497+
}
498+
486499
@Test
487500
public void testDoNotAllowUnknownEnumValuesAsMapKeysWhenReadAsNullDisabled() throws Exception
488501
{

0 commit comments

Comments
 (0)