Skip to content

Commit 143b66f

Browse files
committed
Allow default enums with @JsonCreator
This follows the pattern for READ_UNKNOWN_ENUM_VALUES_AS_NULL from here: https://github.com/FasterXML/jackson-databind/pull/1642/files but for READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE It's more tricky because it wasn't immediately apparent how to get the configured default value on the Creator impl side. I decided to try using the EnumResolver, which works, but am not sure what the repercussions of that are fully though. It works the same way, so only IllegalArgumentExceptions will trigger default behavior, so if you have some other custom creator exception logic that will be unaffected.
1 parent 9325008 commit 143b66f

File tree

5 files changed

+43
-11
lines changed

5 files changed

+43
-11
lines changed

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: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public EnumDeserializer(EnumResolver byNameResolver) {
160160
@Deprecated
161161
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
162162
Class<?> enumClass, AnnotatedMethod factory) {
163-
return deserializerForCreator(config, enumClass, factory, null, null);
163+
return deserializerForCreator(config, enumClass, factory, null, null, null);
164164
}
165165

166166
/**
@@ -172,16 +172,15 @@ public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig c
172172
* @since 2.8
173173
*/
174174
public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
175-
Class<?> enumClass, AnnotatedMethod factory,
176-
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
175+
Class<?> enumClass, AnnotatedMethod factory, ValueInstantiator valueInstantiator,
176+
SettableBeanProperty[] creatorProps, EnumResolver byNameResolver)
177177
{
178178
if (config.canOverrideAccessModifiers()) {
179179
ClassUtil.checkAndFixAccess(factory.getMember(),
180180
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
181181
}
182-
return new FactoryBasedEnumDeserializer(enumClass, factory,
183-
factory.getParameterType(0),
184-
valueInstantiator, creatorProps);
182+
return new FactoryBasedEnumDeserializer(enumClass, factory, factory.getParameterType(0),
183+
valueInstantiator, creatorProps, byNameResolver);
185184
}
186185

187186
/**

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.databind.deser.std;
22

33
import java.io.IOException;
4+
import java.util.Objects;
45

56
import com.fasterxml.jackson.core.JacksonException;
67
import com.fasterxml.jackson.core.JsonParser;
@@ -15,6 +16,7 @@
1516
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1617
import com.fasterxml.jackson.databind.type.LogicalType;
1718
import com.fasterxml.jackson.databind.util.ClassUtil;
19+
import com.fasterxml.jackson.databind.util.EnumResolver;
1820

1921
/**
2022
* Deserializer that uses a single-String static factory method
@@ -34,6 +36,7 @@ class FactoryBasedEnumDeserializer
3436
protected final JsonDeserializer<?> _deser;
3537
protected final ValueInstantiator _valueInstantiator;
3638
protected final SettableBeanProperty[] _creatorProps;
39+
protected final Enum<?> _defaultValue;
3740

3841
protected final boolean _hasArgs;
3942

@@ -45,7 +48,8 @@ class FactoryBasedEnumDeserializer
4548
private transient volatile PropertyBasedCreator _propCreator;
4649

4750
public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType paramType,
48-
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
51+
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps,
52+
EnumResolver enumResolver)
4953
{
5054
super(cls);
5155
_factory = f;
@@ -56,6 +60,13 @@ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType pa
5660
_deser = null;
5761
_valueInstantiator = valueInstantiator;
5862
_creatorProps = creatorProps;
63+
_defaultValue = Objects.nonNull(enumResolver)? enumResolver.getDefaultValue() : null;
64+
}
65+
66+
public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f, JavaType paramType,
67+
ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps)
68+
{
69+
this(cls, f, paramType, valueInstantiator, creatorProps, null);
5970
}
6071

6172
/**
@@ -70,6 +81,7 @@ public FactoryBasedEnumDeserializer(Class<?> cls, AnnotatedMethod f)
7081
_deser = null;
7182
_valueInstantiator = null;
7283
_creatorProps = null;
84+
_defaultValue = null;
7385
}
7486

7587
protected FactoryBasedEnumDeserializer(FactoryBasedEnumDeserializer base,
@@ -80,6 +92,7 @@ protected FactoryBasedEnumDeserializer(FactoryBasedEnumDeserializer base,
8092
_hasArgs = base._hasArgs;
8193
_valueInstantiator = base._valueInstantiator;
8294
_creatorProps = base._creatorProps;
95+
_defaultValue = base._defaultValue;
8396

8497
_deser = deser;
8598
}
@@ -202,6 +215,10 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
202215
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
203216
return null;
204217
}
218+
219+
if (ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
220+
return _defaultValue;
221+
}
205222
// 12-Oct-2021, tatu: Should probably try to provide better exception since
206223
// we likely hit argument incompatibility... Or can this happen?
207224
}

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)