Skip to content

Commit b1a61bb

Browse files
authored
Fixed so that KeyDeserializer set by annotation is not overwritten by KeyDeserializer set in the mapper (#4929)
1 parent 603fd9b commit b1a61bb

File tree

4 files changed

+83
-10
lines changed

4 files changed

+83
-10
lines changed

release-notes/CREDITS-2.x

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,10 @@ wrongwrong (@k163377)
18501850
* Reported #4878: When serializing a Map via Converter(StdDelegatingSerializer),
18511851
a NullPointerException is thrown due to missing key serializer
18521852
(2.18.3)
1853+
* Contributed fix for #4444: The `KeyDeserializer` specified in the class with
1854+
`@JsonDeserialize(keyUsing = ...)` is overwritten by the `KeyDeserializer`
1855+
specified in the `ObjectMapper`.
1856+
(2.18.3)
18531857

18541858
Bernd Ahlers (@bernd)
18551859
* Reported #4742: Deserialization with Builder, External type id, `@JsonCreator` failing

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Project: jackson-databind
66

77
2.18.3 (not yet released)
88

9+
#4444: The `KeyDeserializer` specified in the class with `@JsonDeserialize(keyUsing = ...)`
10+
is overwritten by the `KeyDeserializer` specified in the `ObjectMapper`.
11+
(fix by @wrongwrong)
912
#4827: Subclassed Throwable deserialization fails since v2.18.0 - no creator
1013
index for property 'cause'
1114
(reported by @nilswieber)

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,8 +1269,11 @@ public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
12691269
{
12701270
final DeserializationConfig config = ctxt.getConfig();
12711271
final BeanDescription beanDesc = config.introspectClassAnnotations(type);
1272-
KeyDeserializer deser = null;
1273-
if (_factoryConfig.hasKeyDeserializers()) {
1272+
1273+
// [databind#2452]: Support `@JsonDeserialize(keyUsing = ...)`
1274+
KeyDeserializer deser = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
1275+
1276+
if (deser == null && _factoryConfig.hasKeyDeserializers()) {
12741277
for (KeyDeserializers d : _factoryConfig.keyDeserializers()) {
12751278
deser = d.findKeyDeserializer(type, config, beanDesc);
12761279
if (deser != null) {
@@ -1281,14 +1284,10 @@ public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
12811284

12821285
// the only non-standard thing is this:
12831286
if (deser == null) {
1284-
// [databind#2452]: Support `@JsonDeserialize(keyUsing = ...)`
1285-
deser = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
1286-
if (deser == null) {
1287-
if (type.isEnumType()) {
1288-
deser = _createEnumKeyDeserializer(ctxt, type);
1289-
} else {
1290-
deser = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type);
1291-
}
1287+
if (type.isEnumType()) {
1288+
deser = _createEnumKeyDeserializer(ctxt, type);
1289+
} else {
1290+
deser = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type);
12921291
}
12931292
}
12941293
// and then post-processing
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.fasterxml.jackson.databind.deser.jdk;
2+
3+
import java.io.IOException;
4+
import java.util.Map;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import com.fasterxml.jackson.core.type.TypeReference;
9+
import com.fasterxml.jackson.databind.DeserializationContext;
10+
import com.fasterxml.jackson.databind.KeyDeserializer;
11+
import com.fasterxml.jackson.databind.ObjectMapper;
12+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
13+
import com.fasterxml.jackson.databind.module.SimpleModule;
14+
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
18+
// [databind#4444]
19+
public class CustomMapKeyDeserializer4444Test extends DatabindTestUtil
20+
{
21+
@JsonDeserialize(keyUsing = ForClass.class)
22+
static class MyKey {
23+
private final String value;
24+
25+
MyKey(String value) {
26+
this.value = value;
27+
}
28+
}
29+
30+
static class ForClass extends KeyDeserializer {
31+
@Override
32+
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
33+
return new MyKey(key + "-class");
34+
}
35+
}
36+
37+
static class ForMapper extends KeyDeserializer {
38+
@Override
39+
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
40+
return new MyKey(key + "-mapper");
41+
}
42+
}
43+
44+
// It is not declared as new TypeReference<> because it causes a compile error in Java 8.
45+
TypeReference<Map<MyKey, String>> typeRef = new TypeReference<Map<MyKey, String>>() {
46+
};
47+
48+
@Test
49+
void withoutForClass() throws Exception {
50+
ObjectMapper mapper = newJsonMapper();
51+
Map<MyKey, String> result = mapper.readValue("{\"foo\":null}", typeRef);
52+
53+
assertEquals("foo-class", result.keySet().stream().findFirst().get().value);
54+
}
55+
56+
// The KeyDeserializer set by the annotation must not be overwritten by the KeyDeserializer set in the mapper.
57+
@Test
58+
void withForClass() throws Exception {
59+
SimpleModule sm = new SimpleModule();
60+
sm.addKeyDeserializer(MyKey.class, new ForMapper());
61+
62+
ObjectMapper mapper = jsonMapperBuilder().addModule(sm).build();
63+
Map<MyKey, String> result = mapper.readValue("{\"foo\":null}", typeRef);
64+
65+
assertEquals("foo-class", result.keySet().stream().findFirst().get().value);
66+
}
67+
}

0 commit comments

Comments
 (0)