Skip to content

Commit 09e5ba1

Browse files
committed
Fix #1565
1 parent 170a414 commit 09e5ba1

File tree

6 files changed

+164
-9
lines changed

6 files changed

+164
-9
lines changed

release-notes/VERSION-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.9.6 (not yet released)
8+
9+
#1565: Deserialization failure with Polymorphism using JsonTypeInfo `defaultImpl`,
10+
subtype as target
11+
712
2.9.5 (26-Mar-2018)
813

914
#1911: Allow serialization of `BigDecimal` as String, using

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
1010
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
1111
import com.fasterxml.jackson.core.JsonLocation;
12+
import com.fasterxml.jackson.core.JsonParser;
1213
import com.fasterxml.jackson.databind.*;
1314
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
1415
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
1516
import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
1617
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
1718
import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
1819
import com.fasterxml.jackson.databind.deser.std.*;
20+
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
1921
import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
2022
import com.fasterxml.jackson.databind.introspect.*;
2123
import com.fasterxml.jackson.databind.jsontype.NamedType;
@@ -1554,9 +1556,8 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
15541556
AnnotationIntrospector ai = config.getAnnotationIntrospector();
15551557
TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType);
15561558

1557-
/* Ok: if there is no explicit type info handler, we may want to
1558-
* use a default. If so, config object knows what to use.
1559-
*/
1559+
// Ok: if there is no explicit type info handler, we may want to
1560+
// use a default. If so, config object knows what to use.
15601561
Collection<NamedType> subtypes = null;
15611562
if (b == null) {
15621563
b = config.getDefaultTyper(baseType);
@@ -1574,7 +1575,16 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
15741575
b = b.defaultImpl(defaultType.getRawClass());
15751576
}
15761577
}
1577-
return b.buildTypeDeserializer(config, baseType, subtypes);
1578+
// 05-Apt-2018, tatu: Since we get non-mapping exception due to various limitations,
1579+
// map to better type here
1580+
try {
1581+
return b.buildTypeDeserializer(config, baseType, subtypes);
1582+
} catch (IllegalArgumentException e0) {
1583+
InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null,
1584+
e0.getMessage(), baseType);
1585+
e.initCause(e0);
1586+
throw e;
1587+
}
15781588
}
15791589

15801590
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public abstract JsonDeserializer<?> createMapLikeDeserializer(DeserializationCon
184184
public abstract KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
185185
JavaType type)
186186
throws JsonMappingException;
187-
187+
188188
/**
189189
* Method called to find and create a type information deserializer for given base type,
190190
* if one is needed. If not needed (no polymorphic handling configured for type),

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.fasterxml.jackson.databind.annotation.NoClass;
99
import com.fasterxml.jackson.databind.cfg.MapperConfig;
1010
import com.fasterxml.jackson.databind.jsontype.*;
11-
import com.fasterxml.jackson.databind.util.ClassUtil;
1211

1312
/**
1413
* Default {@link TypeResolverBuilder} implementation.
@@ -143,13 +142,17 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
143142
defaultImpl = config.getTypeFactory()
144143
.constructSpecializedType(baseType, _defaultImpl);
145144
} else {
146-
// 05-Apr-2018, tatu: [databind#1861] Not sure what would be the best way
147-
// to handle, but for 2.9, let's consider case of "sibling" defaultImpl...
148-
// ... Ugh. Not declared to throw `JsonMappingException`, so...
145+
// 05-Apr-2018, tatu: As [databind#1565] and [databind#1861] need to allow
146+
// some cases of seemingly incompatible `defaultImpl`. Easiest to just clear
147+
// the setting.
148+
149+
/*
149150
throw new IllegalArgumentException(
150151
String.format("Invalid \"defaultImpl\" (%s): not a subtype of basetype (%s)",
151152
ClassUtil.nameOf(_defaultImpl), ClassUtil.nameOf(baseType.getRawClass()))
152153
);
154+
*/
155+
defaultImpl = null;
153156
}
154157
}
155158
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.fasterxml.jackson.databind.jsontype;
2+
3+
import com.fasterxml.jackson.annotation.*;
4+
5+
import com.fasterxml.jackson.databind.*;
6+
7+
public class TestPolymorphicWithDefaultImpl1565 extends BaseMapTest
8+
{
9+
// [databind#1565]
10+
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,
11+
property="typeInfo", defaultImpl = CBaseClass1565.class)
12+
@JsonSubTypes({
13+
@JsonSubTypes.Type(CDerived1565.class)
14+
})
15+
public static interface CTestInterface1565
16+
{
17+
public String getName();
18+
public void setName(String name);
19+
public String getTypeInfo();
20+
}
21+
22+
static class CBaseClass1565 implements CTestInterface1565
23+
{
24+
private String mName;
25+
26+
@Override
27+
public String getName() {
28+
return(mName);
29+
}
30+
31+
@Override
32+
public void setName(String name) {
33+
mName = name;
34+
}
35+
36+
@Override
37+
public String getTypeInfo() {
38+
return "base";
39+
}
40+
}
41+
42+
@JsonTypeName("derived")
43+
static class CDerived1565 extends CBaseClass1565
44+
{
45+
public String description;
46+
47+
@Override
48+
public String getTypeInfo() {
49+
return "derived";
50+
}
51+
}
52+
53+
// [databind#1861]
54+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultImpl1861.class)
55+
@JsonSubTypes({
56+
@JsonSubTypes.Type(name = "a", value = Impl1861A.class)
57+
})
58+
static abstract class Bean1861 {
59+
public String base;
60+
}
61+
62+
static class DefaultImpl1861 extends Bean1861 {
63+
public int id;
64+
}
65+
66+
static class Impl1861A extends Bean1861 {
67+
public int valueA;
68+
}
69+
70+
/*
71+
/**********************************************************************
72+
/* Test methods
73+
/**********************************************************************
74+
*/
75+
76+
private final ObjectMapper MAPPER = new ObjectMapper();
77+
78+
// [databind#1565]
79+
public void testIncompatibleDefaultImpl1565() throws Exception
80+
{
81+
String value = "{\"typeInfo\": \"derived\", \"name\": \"John\", \"description\": \"Owner\"}";
82+
CDerived1565 result = MAPPER.readValue(value, CDerived1565.class);
83+
assertNotNull(result);
84+
}
85+
86+
// [databind#1861]
87+
public void testWithIncompatibleTargetType1861() throws Exception
88+
{
89+
// Should allow deserialization even if `defaultImpl` incompatible
90+
Impl1861A result = MAPPER.readValue(aposToQuotes("{'type':'a','base':'foo','valueA':3}"),
91+
Impl1861A.class);
92+
assertNotNull(result);
93+
}
94+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.fasterxml.jackson.failing;
2+
3+
import java.util.*;
4+
5+
import com.fasterxml.jackson.databind.*;
6+
7+
@SuppressWarnings("serial")
8+
public class SubTypeResolution1964Test extends BaseMapTest
9+
{
10+
static class AccessModel {
11+
private Map<Object, Collection<String>> repositoryPrivileges;
12+
13+
public AccessModel() {
14+
repositoryPrivileges = new HashMap<>();
15+
}
16+
17+
public Map<Object, Collection<String>> getRepositoryPrivileges() {
18+
return repositoryPrivileges;
19+
}
20+
21+
public void setRepositoryPrivileges(Map<Object, Collection<String>> repositoryPrivileges) {
22+
this.repositoryPrivileges = repositoryPrivileges;
23+
}
24+
}
25+
26+
static class CustomMap<T> extends LinkedHashMap<Object, T> { }
27+
28+
public void testTypeCompatibility1964() throws Exception
29+
{
30+
Map<Object, Collection<String>> repoPrivilegesMap = new CustomMap<>();
31+
String key = "/storages/storage0/releases";
32+
Collection<String> values = new HashSet<>();
33+
values.add("ARTIFACTS_RESOLVE");
34+
repoPrivilegesMap.put(key, values);
35+
36+
AccessModel accessModel = new AccessModel();
37+
accessModel.setRepositoryPrivileges(repoPrivilegesMap);
38+
39+
ObjectMapper mapper = new ObjectMapper();
40+
String jsonStr = mapper.writeValueAsString(accessModel);
41+
assertNotNull(jsonStr);
42+
}
43+
}

0 commit comments

Comments
 (0)