Skip to content

Commit 69db038

Browse files
committed
Merge branch '2.19'
2 parents 39e0ef9 + 59a2e22 commit 69db038

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

release-notes/CREDITS-2.x

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

18571861
Bernd Ahlers (@bernd)
18581862
* 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
@@ -46,6 +46,9 @@ Project: jackson-databind
4646
4747
2.18.3 (not yet released)
4848
49+
#4444: The `KeyDeserializer` specified in the class with `@JsonDeserialize(keyUsing = ...)`
50+
is overwritten by the `KeyDeserializer` specified in the `ObjectMapper`.
51+
(fix by @wrongwrong)
4952
#4827: Subclassed Throwable deserialization fails since v2.18.0 - no creator
5053
index for property 'cause'
5154
(reported by @nilswieber)

src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,8 +1207,11 @@ public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
12071207
{
12081208
final DeserializationConfig config = ctxt.getConfig();
12091209
final BeanDescription beanDesc = ctxt.introspectBeanDescription(type);
1210-
KeyDeserializer deser = null;
1211-
if (_factoryConfig.hasKeyDeserializers()) {
1210+
1211+
// [databind#2452]: Support `@JsonDeserialize(keyUsing = ...)`
1212+
KeyDeserializer deser = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
1213+
1214+
if ((deser == null) && _factoryConfig.hasKeyDeserializers()) {
12121215
for (KeyDeserializers d : _factoryConfig.keyDeserializers()) {
12131216
deser = d.findKeyDeserializer(type, config, beanDesc);
12141217
if (deser != null) {
@@ -1219,14 +1222,10 @@ public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
12191222

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

0 commit comments

Comments
 (0)