Skip to content

Commit dfaca81

Browse files
committed
Fix #1809
1 parent a1404d5 commit dfaca81

File tree

3 files changed

+71
-9
lines changed

3 files changed

+71
-9
lines changed

release-notes/VERSION

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ Project: jackson-databind
1111
#1768: Improve `TypeFactory.constructFromCanonical()` to work with
1212
`java.lang.reflect.Type.getTypeName()` format
1313
#1804: `ValueInstantiator.canInstantiate()` ignores `canCreateUsingArrayDelegate()`
14-
(reported byb henryptung@github)
14+
(reported by henryptung@github)
15+
#1807: Jackson-databind caches plain map deserializer and use it even map has `@JsonDeserializer`
16+
(reported by lexas2509@github)
1517

1618
2.8.10 (24-Aug-2017)
1719

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ protected JsonDeserializer<Object> _findCachedDeserializer(JavaType type)
204204
if (type == null) {
205205
throw new IllegalArgumentException("Null JavaType passed");
206206
}
207-
if (_hasCustomValueHandler(type)) {
207+
if (_hasCustomHandlers(type)) {
208208
return null;
209209
}
210210
return _cachedDeserializers.get(type);
@@ -274,7 +274,7 @@ protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
274274
* (but can be re-defined for sub-classes by using @JsonCachable!)
275275
*/
276276
// 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers
277-
boolean addToCache = !_hasCustomValueHandler(type) && deser.isCachable();
277+
boolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();
278278

279279
/* we will temporarily hold on to all created deserializers (to
280280
* handle cyclic references, and possibly reuse non-cached
@@ -531,13 +531,23 @@ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt,
531531
* Helper method used to prevent both caching and cache lookups for structured
532532
* types that have custom value handlers
533533
*
534-
* @since 2.4.6
534+
* @since 2.8.11
535535
*/
536-
private boolean _hasCustomValueHandler(JavaType t) {
536+
private boolean _hasCustomHandlers(JavaType t) {
537537
if (t.isContainerType()) {
538+
// First: value types may have both value and type handlers
538539
JavaType ct = t.getContentType();
539540
if (ct != null) {
540-
return (ct.getValueHandler() != null) || (ct.getTypeHandler() != null);
541+
if ((ct.getValueHandler() != null) || (ct.getTypeHandler() != null)) {
542+
return true;
543+
}
544+
}
545+
// Second: map(-like) types may have value handler for key (but not type; keys are untyped)
546+
if (t.isMapLikeType()) {
547+
JavaType kt = t.getKeyType();
548+
if (kt.getValueHandler() != null) {
549+
return true;
550+
}
541551
}
542552
}
543553
return false;
@@ -568,9 +578,7 @@ private Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClas
568578
protected JsonDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)
569579
throws JsonMappingException
570580
{
571-
/* Let's try to figure out the reason, to give better error
572-
* messages
573-
*/
581+
// Let's try to figure out the reason, to give better error messages
574582
Class<?> rawClass = type.getRawClass();
575583
if (!ClassUtil.isConcrete(rawClass)) {
576584
ctxt.reportMappingException("Can not find a Value deserializer for abstract type %s", type);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.fasterxml.jackson.databind.deser.jdk;
2+
3+
import java.io.IOException;
4+
import java.util.Map;
5+
import java.util.TreeMap;
6+
7+
import com.fasterxml.jackson.databind.*;
8+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
9+
10+
// for [databind#1807]
11+
public class MapDeserializerCachingTest extends BaseMapTest
12+
{
13+
public static class NonAnnotatedMapHolderClass {
14+
public Map<String, String> data = new TreeMap<String, String>();
15+
}
16+
17+
public static class MapHolder {
18+
@JsonDeserialize(keyUsing = MyKeyDeserializer.class)
19+
public Map<String, String> data = new TreeMap<String, String>();
20+
}
21+
22+
public static class MyKeyDeserializer extends KeyDeserializer {
23+
@Override
24+
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
25+
return key + " (CUSTOM)";
26+
}
27+
}
28+
29+
/*
30+
/**********************************************************
31+
/* Test methods
32+
/**********************************************************
33+
*/
34+
35+
public void testCachedSerialize() throws IOException {
36+
ObjectMapper mapper = new ObjectMapper();
37+
String json = aposToQuotes("{'data':{'1st':'onedata','2nd':'twodata'}}");
38+
39+
// Do deserialization with non-annotated map property
40+
NonAnnotatedMapHolderClass ignored = mapper.readValue(json, NonAnnotatedMapHolderClass.class);
41+
assertTrue(ignored.data.containsKey("1st"));
42+
assertTrue(ignored.data.containsKey("2nd"));
43+
44+
//mapper = new ObjectMapper();
45+
46+
MapHolder model2 = mapper.readValue(json, MapHolder.class);
47+
if (!model2.data.containsKey("1st (CUSTOM)")
48+
|| !model2.data.containsKey("2nd (CUSTOM)")) {
49+
fail("Not using custom key deserializer for input: "+json+"; resulted in: "+model2.data);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)