Skip to content

Commit 7d8692c

Browse files
authored
Fix #4214: support polymorphic deserialization of EnumSet (#4222)
1 parent 35bd198 commit 7d8692c

File tree

8 files changed

+116
-64
lines changed

8 files changed

+116
-64
lines changed

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ Project: jackson-databind
99
#4209: Make `BeanDeserializerModifier`/`BeanSerializerModifier`
1010
implement `java.io.Serializable`
1111
(fix contributed by Muhammad K)
12+
#4214: `EnumSet` deserialization does not work when we activate
13+
default typing in `ObjectMapper`
14+
(reported by @dvhvsekhar)
1215

1316
2.16.1 (not yet released)
1417

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,8 @@ public JsonDeserializer<?> createCollectionDeserializer(DeserializationContext c
13951395
if (contentDeser == null) { // not defined by annotation
13961396
// One special type: EnumSet:
13971397
if (EnumSet.class.isAssignableFrom(collectionClass)) {
1398-
deser = new EnumSetDeserializer(contentType, null);
1398+
deser = new EnumSetDeserializer(contentType, null,
1399+
contentTypeDeser);
13991400
}
14001401
}
14011402
}

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

+59-14
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,23 @@ public class EnumSetDeserializer
2626
extends StdDeserializer<EnumSet<?>>
2727
implements ContextualDeserializer
2828
{
29-
private static final long serialVersionUID = 1L; // since 2.5
29+
private static final long serialVersionUID = 2L; // since 2.17
3030

3131
protected final JavaType _enumType;
3232

3333
protected JsonDeserializer<Enum<?>> _enumDeserializer;
3434

35+
/**
36+
* If element instances have polymorphic type information, this
37+
* is the type deserializer that can handle it.
38+
*<p>
39+
* NOTE: only added in 2.17 due to new {@code DefaultType} choices
40+
* that allow polymorphic deserialization of {@code Enum} types.
41+
*
42+
* @since 2.17
43+
*/
44+
protected final TypeDeserializer _valueTypeDeserializer;
45+
3546
/**
3647
* Handler we need for dealing with nulls.
3748
*
@@ -62,8 +73,12 @@ public class EnumSetDeserializer
6273
/**********************************************************
6374
*/
6475

76+
/**
77+
* @since 2.17
78+
*/
6579
@SuppressWarnings("unchecked" )
66-
public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
80+
public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser,
81+
TypeDeserializer valueTypeDeser)
6782
{
6883
super(EnumSet.class);
6984
_enumType = enumType;
@@ -72,11 +87,21 @@ public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
7287
throw new IllegalArgumentException("Type "+enumType+" not Java Enum type");
7388
}
7489
_enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
90+
_valueTypeDeserializer = valueTypeDeser;
7591
_unwrapSingle = null;
7692
_nullProvider = null;
7793
_skipNullValues = false;
7894
}
7995

96+
/**
97+
* @deprecated Since 2.17
98+
*/
99+
@Deprecated
100+
public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
101+
{
102+
this(enumType, deser, null);
103+
}
104+
80105
/**
81106
* @since 2.7
82107
* @deprecated Since 2.10.1
@@ -96,6 +121,7 @@ protected EnumSetDeserializer(EnumSetDeserializer base,
96121
super(base);
97122
_enumType = base._enumType;
98123
_enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
124+
_valueTypeDeserializer = base._valueTypeDeserializer;
99125
_nullProvider = nuller;
100126
_skipNullValues = NullsConstantProvider.isSkipper(nuller);
101127
_unwrapSingle = unwrapSingle;
@@ -108,22 +134,30 @@ public EnumSetDeserializer withDeserializer(JsonDeserializer<?> deser) {
108134
return new EnumSetDeserializer(this, deser, _nullProvider, _unwrapSingle);
109135
}
110136

111-
@Deprecated // since 2.10.1
112-
public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, Boolean unwrapSingle) {
113-
return withResolved(deser, _nullProvider, unwrapSingle);
114-
}
115-
116137
/**
117138
* @since 2.10.1
118139
*/
119-
public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, NullValueProvider nuller,
120-
Boolean unwrapSingle) {
121-
if ((Objects.equals(_unwrapSingle, unwrapSingle)) && (_enumDeserializer == deser) && (_nullProvider == deser)) {
140+
public EnumSetDeserializer withResolved(JsonDeserializer<?> deser,
141+
TypeDeserializer valueTypeDeser,
142+
NullValueProvider nuller, Boolean unwrapSingle) {
143+
if ((Objects.equals(_unwrapSingle, unwrapSingle))
144+
&& (_enumDeserializer == deser)
145+
&& (_valueTypeDeserializer == valueTypeDeser)
146+
&& (_nullProvider == deser)) {
122147
return this;
123148
}
124149
return new EnumSetDeserializer(this, deser, nuller, unwrapSingle);
125150
}
126151

152+
/**
153+
* @deprecated Since 2.17
154+
*/
155+
@Deprecated
156+
public EnumSetDeserializer withResolved(JsonDeserializer<?> deser,
157+
NullValueProvider nuller, Boolean unwrapSingle) {
158+
return withResolved(deser, _valueTypeDeserializer, nuller, unwrapSingle);
159+
}
160+
127161
/*
128162
/**********************************************************
129163
/* Basic metadata
@@ -137,12 +171,14 @@ public EnumSetDeserializer withResolved(JsonDeserializer<?> deser, NullValueProv
137171
@Override
138172
public boolean isCachable() {
139173
// One caveat: content deserializer should prevent caching
140-
if (_enumType.getValueHandler() != null) {
174+
if ((_enumType.getValueHandler() != null)
175+
// Another: polymorphic deserialization
176+
|| (_valueTypeDeserializer != null)) {
141177
return false;
142178
}
143179
return true;
144180
}
145-
181+
146182
@Override // since 2.12
147183
public LogicalType logicalType() {
148184
return LogicalType.Collection;
@@ -184,7 +220,13 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
184220
} else { // if directly assigned, probably not yet contextual, so:
185221
deser = ctxt.handleSecondaryContextualization(deser, property, _enumType);
186222
}
187-
return withResolved(deser, findContentNullProvider(ctxt, property, deser), unwrapSingle);
223+
// and finally, type deserializer needs context as well
224+
TypeDeserializer valueTypeDeser = _valueTypeDeserializer;
225+
if (valueTypeDeser != null) {
226+
valueTypeDeser = valueTypeDeser.forProperty(property);
227+
}
228+
return withResolved(deser, valueTypeDeser,
229+
findContentNullProvider(ctxt, property, deser), unwrapSingle);
188230
}
189231

190232
/*
@@ -220,6 +262,7 @@ protected final EnumSet<?> _deserialize(JsonParser p, DeserializationContext ctx
220262
EnumSet result) throws IOException
221263
{
222264
JsonToken t;
265+
final TypeDeserializer typeDeser = _valueTypeDeserializer;
223266

224267
try {
225268
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
@@ -232,8 +275,10 @@ protected final EnumSet<?> _deserialize(JsonParser p, DeserializationContext ctx
232275
continue;
233276
}
234277
value = (Enum<?>) _nullProvider.getNullValue(ctxt);
235-
} else {
278+
} else if (typeDeser == null) {
236279
value = _enumDeserializer.deserialize(p, ctxt);
280+
} else {
281+
value = (Enum<?>) _enumDeserializer.deserializeWithType(p, ctxt, typeDeser);
237282
}
238283
if (value != null) {
239284
result.add(value);

src/test/java/com/fasterxml/jackson/databind/cfg/CacheProviderTest.java

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public LookupCache<Object, JavaType> forTypeFactory() {
121121
return new LRUMap<>(16, 64);
122122
}
123123

124+
@Override
124125
public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config) {
125126
return _cache;
126127
}
@@ -154,6 +155,7 @@ public LookupCache<Object, JavaType> forTypeFactory() {
154155
return _cache;
155156
}
156157

158+
@Override
157159
public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config) {
158160
return new LRUMap<>(16, 64);
159161
}

src/test/java/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier4216Test.java

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ private static SimpleModule getSimpleModuleWithCounter(AtomicInteger counter) {
4848
SimpleModule module = new SimpleModule();
4949
module.setDeserializerModifier(
5050
new BeanDeserializerModifier() {
51+
private static final long serialVersionUID = 1L;
52+
5153
@Override
5254
public JsonDeserializer<?> modifyArrayDeserializer(DeserializationConfig config,
5355
ArrayType valueType, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.fasterxml.jackson.databind.deser.enums;
2+
3+
import java.util.EnumSet;
4+
import java.util.Objects;
5+
import java.util.Set;
6+
7+
import com.fasterxml.jackson.databind.BaseMapTest;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
10+
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
11+
12+
// For [databind#4214]
13+
public class EnumSetPolymorphicDeser4214Test extends BaseMapTest
14+
{
15+
static enum MyEnum {
16+
ITEM_A, ITEM_B;
17+
}
18+
19+
static class EnumSetHolder {
20+
public Set<MyEnum> enumSet; // use Set instead of EnumSet for type of this
21+
22+
@Override
23+
public boolean equals(Object o) {
24+
if (!(o instanceof EnumSetHolder)) {
25+
return false;
26+
}
27+
EnumSetHolder eh = (EnumSetHolder) o;
28+
return Objects.equals(enumSet, eh.enumSet);
29+
}
30+
}
31+
32+
public void testPolymorphicDeserialization4214() throws Exception
33+
{
34+
// Need to use Default Typing to trigger issue
35+
ObjectMapper mapper = jsonMapperBuilder()
36+
.activateDefaultTyping(BasicPolymorphicTypeValidator.builder().allowIfBaseType(Object.class).build(),
37+
DefaultTyping.NON_FINAL_AND_ENUMS)
38+
.build();
39+
40+
EnumSetHolder enumSetHolder = new EnumSetHolder();
41+
enumSetHolder.enumSet = EnumSet.allOf(MyEnum.class);
42+
String json = mapper.writeValueAsString(enumSetHolder);
43+
EnumSetHolder result = mapper.readValue(json, EnumSetHolder.class);
44+
assertEquals(result, enumSetHolder);
45+
}
46+
}

src/test/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier1612Test.java

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public Bean1612(Integer a, Integer b, Double c) {
2424
}
2525

2626
static class Modifier1612 extends BeanSerializerModifier {
27+
private static final long serialVersionUID = 1L;
28+
2729
@Override
2830
public BeanSerializerBuilder updateBuilder(SerializationConfig config, BeanDescription beanDesc,
2931
BeanSerializerBuilder builder) {

src/test/java/com/fasterxml/jackson/failing/EnumSetSerialization4214Test.java

-49
This file was deleted.

0 commit comments

Comments
 (0)