Skip to content

Commit a5c4632

Browse files
authored
Fix deduction deserializer with DefaultTypeResolverBuilder (pending #) (#3505)
1 parent 492334d commit a5c4632

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,15 +1784,14 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
17841784

17851785
// Ok: if there is no explicit type info handler, we may want to
17861786
// use a default. If so, config object knows what to use.
1787-
Collection<NamedType> subtypes = null;
17881787
if (b == null) {
17891788
b = config.getDefaultTyper(baseType);
17901789
if (b == null) {
17911790
return null;
17921791
}
1793-
} else {
1794-
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac);
17951792
}
1793+
final Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, ac);
1794+
17961795
// May need to figure out default implementation, if none found yet
17971796
// (note: check for abstract type is not 100% mandatory, more of an optimization)
17981797
if ((b.getDefaultImpl() == null) && baseType.isAbstract()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.fasterxml.jackson.databind.deser;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
import com.fasterxml.jackson.databind.BaseMapTest;
5+
import com.fasterxml.jackson.databind.DeserializationConfig;
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
9+
import com.fasterxml.jackson.databind.jsontype.NamedType;
10+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
11+
12+
import java.util.ArrayList;
13+
import java.util.Collection;
14+
import java.util.List;
15+
16+
// Tests for [databind#pending] causing a NPE when setting a DefaultTypeResolverBuilder
17+
// and registering subtypes through ObjectMapper (no annotations)
18+
public class TestDeserializerFactoryWithDefaultTypeResolverBuilder extends BaseMapTest {
19+
20+
private interface Parent {
21+
class ChildOne implements Parent {
22+
public String one;
23+
}
24+
25+
class ChildTwo implements Parent {
26+
public String two;
27+
}
28+
}
29+
30+
// This class is technically not needed for the test to fail without the fix
31+
// (AsDeductionTypeDeserializer will crash in #buildFingerprints), but was
32+
// added to have more assertions on the subtypes values
33+
private static final class AssertingTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
34+
35+
public AssertingTypeResolverBuilder() {
36+
super(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS,
37+
BasicPolymorphicTypeValidator.builder().allowIfSubType(Parent.class).build());
38+
}
39+
40+
@Override
41+
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
42+
// this also gets called for String, not sure if that's expected with PTV
43+
// this behaviour is outside the scope of this test
44+
if (baseType.isAbstract()) {
45+
assertNotNull(subtypes);
46+
assertEquals(2, subtypes.size());
47+
final List<NamedType> expected = new ArrayList<>(2);
48+
expected.add(new NamedType(Parent.ChildOne.class));
49+
expected.add(new NamedType(Parent.ChildTwo.class));
50+
assertTrue(subtypes.containsAll(expected));
51+
}
52+
53+
return super.buildTypeDeserializer(config, baseType, subtypes);
54+
}
55+
}
56+
57+
public void testDeductionWithDefaultTypeResolverBuilder() {
58+
final ObjectMapper mapper = jsonMapperBuilder()
59+
.registerSubtypes(Parent.ChildOne.class, Parent.ChildTwo.class)
60+
.setDefaultTyping(new AssertingTypeResolverBuilder()
61+
.init(JsonTypeInfo.Id.DEDUCTION, null))
62+
.build();
63+
64+
try {
65+
final Parent firstRead = mapper.readValue("{ \"one\": \"Hello World\" }", Parent.class);
66+
assertNotNull(firstRead);
67+
assertTrue(firstRead instanceof Parent.ChildOne);
68+
assertEquals("Hello World", ((Parent.ChildOne) firstRead).one);
69+
final Parent secondRead = mapper.readValue("{ \"two\": \"Hello World\" }", Parent.class);
70+
assertNotNull(secondRead);
71+
assertTrue(secondRead instanceof Parent.ChildTwo);
72+
assertEquals("Hello World", ((Parent.ChildTwo) secondRead).two);
73+
} catch (Exception e) {
74+
fail("This call should not throw");
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)