Skip to content

Commit 9065be2

Browse files
authored
Add new DefaultTyping.NON_FINAL_AND_ENUMS to allow default type handler for Enums (#4159)
1 parent 597d1bb commit 9065be2

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java

+24
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ public enum DefaultTyping {
189189
*/
190190
NON_FINAL,
191191

192+
/**
193+
* Enables default typing for non-final types as {@link #NON_FINAL},
194+
* but also includes Enums.
195+
* Designed to allow default typing of Enums without resorting to
196+
* {@link #EVERYTHING}, which has security implications.
197+
*<p>
198+
* @since 2.16
199+
*/
200+
NON_FINAL_AND_ENUMS,
201+
192202
/**
193203
* Value that means that default typing will be used for
194204
* all types, with exception of small number of
@@ -355,6 +365,20 @@ public boolean useForType(JavaType t)
355365
}
356366
// [databind#88] Should not apply to JSON tree models:
357367
return !t.isFinal() && !TreeNode.class.isAssignableFrom(t.getRawClass());
368+
369+
case NON_FINAL_AND_ENUMS: // since 2.16
370+
while (t.isArrayType()) {
371+
t = t.getContentType();
372+
}
373+
// 19-Apr-2016, tatu: ReferenceType like Optional also requires similar handling:
374+
while (t.isReferenceType()) {
375+
t = t.getReferencedType();
376+
}
377+
// [databind#88] Should not apply to JSON tree models:
378+
return (!t.isFinal() && !TreeNode.class.isAssignableFrom(t.getRawClass()))
379+
// [databind#3569] Allow use of default typing for Enums
380+
|| t.isEnumType();
381+
358382
case EVERYTHING:
359383
// So, excluding primitives (handled earlier) and "Natural types" (handled
360384
// before this method is called), applied to everything

src/test/java/com/fasterxml/jackson/databind/jsontype/deftyping/TestDefaultForEnums.java

+53
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.fasterxml.jackson.databind.jsontype.deftyping;
22

3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.core.type.TypeReference;
7+
import com.fasterxml.jackson.databind.JavaType;
8+
import com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator;
39
import java.util.concurrent.TimeUnit;
410

511
import com.fasterxml.jackson.databind.BaseMapTest;
@@ -26,6 +32,25 @@ protected static class TimeUnitBean {
2632
public TimeUnit timeUnit;
2733
}
2834

35+
static class Foo3569<T> {
36+
public T item;
37+
}
38+
39+
enum Bar3569 {
40+
ENABLED, DISABLED, HIDDEN;
41+
42+
@JsonCreator
43+
public static Bar3569 fromValue(String value) {
44+
String upperVal = value.toUpperCase();
45+
for (Bar3569 enumValue : Bar3569.values()) {
46+
if (enumValue.name().equals(upperVal)) {
47+
return enumValue;
48+
}
49+
}
50+
throw new IllegalArgumentException("Bad input [" + value + "]");
51+
}
52+
}
53+
2954
/*
3055
/**********************************************************
3156
/* Test methods
@@ -78,4 +103,32 @@ public void testSimpleEnumsAsField() throws Exception
78103
EnumHolder holder = m.readValue(json, EnumHolder.class);
79104
assertSame(TestEnum.B, holder.value);
80105
}
106+
107+
/**
108+
* [databind#3569]: Unable to deserialize enum object with default-typed
109+
* {@link com.fasterxml.jackson.annotation.JsonTypeInfo.As#WRAPPER_ARRAY} and {@link JsonCreator} together,
110+
*
111+
* @since 2.16
112+
*/
113+
public void testEnumAsWrapperArrayWithCreator() throws JsonProcessingException
114+
{
115+
ObjectMapper objectMapper = jsonMapperBuilder()
116+
.activateDefaultTyping(
117+
new DefaultBaseTypeLimitingValidator(),
118+
ObjectMapper.DefaultTyping.NON_FINAL_AND_ENUMS,
119+
JsonTypeInfo.As.WRAPPER_ARRAY)
120+
.build();
121+
122+
Foo3569<Bar3569> expected = new Foo3569<>();
123+
expected.item = Bar3569.ENABLED;
124+
125+
// First, serialize
126+
String serialized = objectMapper.writeValueAsString(expected);
127+
128+
// Then, deserialize with TypeReference
129+
assertNotNull(objectMapper.readValue(serialized, new TypeReference<Foo3569<Bar3569>>() {}));
130+
// And, also try as described in [databind#3569]
131+
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(Foo3569.class, new Class[]{Bar3569.class});
132+
assertNotNull(objectMapper.readValue(serialized, javaType));
133+
}
81134
}

0 commit comments

Comments
 (0)