Skip to content

Commit 69416ee

Browse files
mikeldplcowtowncoder
authored andcommitted
Fix #955. Added DeserializationFeature USE BASE TYPE AS DEFAULT (#2036)
1 parent 324b2d7 commit 69416ee

File tree

3 files changed

+131
-21
lines changed

3 files changed

+131
-21
lines changed

src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,14 @@ public enum DeserializationFeature implements ConfigFeature
459459
*/
460460
ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true),
461461

462+
/**
463+
* Feature that specifies whether the given concrete class is used
464+
* if type property is missing.
465+
*
466+
* @since 2.9
467+
*/
468+
USE_BASE_TYPE_AS_DEFAULT(false),
469+
462470
/*
463471
/******************************************************
464472
/* Other

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

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,36 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
120120

121121
TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
122122

123-
JavaType defaultImpl;
123+
JavaType defaultImpl = defineDefaultImpl(config, baseType);
124124

125+
// First, method for converting type info to type id:
126+
switch (_includeAs) {
127+
case WRAPPER_ARRAY:
128+
return new AsArrayTypeDeserializer(baseType, idRes,
129+
_typeProperty, _typeIdVisible, defaultImpl);
130+
case PROPERTY:
131+
case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
132+
return new AsPropertyTypeDeserializer(baseType, idRes,
133+
_typeProperty, _typeIdVisible, defaultImpl, _includeAs);
134+
case WRAPPER_OBJECT:
135+
return new AsWrapperTypeDeserializer(baseType, idRes,
136+
_typeProperty, _typeIdVisible, defaultImpl);
137+
case EXTERNAL_PROPERTY:
138+
return new AsExternalTypeDeserializer(baseType, idRes,
139+
_typeProperty, _typeIdVisible, defaultImpl);
140+
}
141+
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
142+
}
143+
144+
protected JavaType defineDefaultImpl(DeserializationConfig config, JavaType baseType) {
145+
JavaType defaultImpl;
125146
if (_defaultImpl == null) {
126-
defaultImpl = null;
147+
//Fis of issue #955
148+
if (config.isEnabled(DeserializationFeature.USE_BASE_TYPE_AS_DEFAULT) && !baseType.isAbstract()) {
149+
defaultImpl = baseType;
150+
} else {
151+
defaultImpl = null;
152+
}
127153
} else {
128154
// 20-Mar-2016, tatu: It is important to do specialization go through
129155
// TypeFactory to ensure proper resolution; with 2.7 and before, direct
@@ -132,7 +158,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
132158
// if so, need to add explicit checks for marker types. Not ideal, but
133159
// seems like a reasonable compromise.
134160
if ((_defaultImpl == Void.class)
135-
|| (_defaultImpl == NoClass.class)) {
161+
|| (_defaultImpl == NoClass.class)) {
136162
defaultImpl = config.getTypeFactory().constructType(_defaultImpl);
137163
} else {
138164
if (baseType.hasRawClass(_defaultImpl)) { // common enough to check
@@ -156,24 +182,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
156182
}
157183
}
158184
}
159-
160-
// First, method for converting type info to type id:
161-
switch (_includeAs) {
162-
case WRAPPER_ARRAY:
163-
return new AsArrayTypeDeserializer(baseType, idRes,
164-
_typeProperty, _typeIdVisible, defaultImpl);
165-
case PROPERTY:
166-
case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
167-
return new AsPropertyTypeDeserializer(baseType, idRes,
168-
_typeProperty, _typeIdVisible, defaultImpl, _includeAs);
169-
case WRAPPER_OBJECT:
170-
return new AsWrapperTypeDeserializer(baseType, idRes,
171-
_typeProperty, _typeIdVisible, defaultImpl);
172-
case EXTERNAL_PROPERTY:
173-
return new AsExternalTypeDeserializer(baseType, idRes,
174-
_typeProperty, _typeIdVisible, defaultImpl);
175-
}
176-
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
185+
return defaultImpl;
177186
}
178187

179188
/*
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.fasterxml.jackson.databind.jsontype;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
import com.fasterxml.jackson.databind.BaseMapTest;
5+
import com.fasterxml.jackson.databind.DeserializationFeature;
6+
import com.fasterxml.jackson.databind.JsonMappingException;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
9+
import java.io.IOException;
10+
11+
public class TestBaseTypeAsDefault extends BaseMapTest {
12+
13+
private ObjectMapper objectMapper;
14+
15+
@Override
16+
public void setUp() {
17+
objectMapper = new ObjectMapper();
18+
objectMapper.enable(DeserializationFeature.USE_BASE_TYPE_AS_DEFAULT);
19+
}
20+
21+
public void testPositiveForParent() throws IOException {
22+
Object o = objectMapper.readerFor(Parent.class).readValue("{}");
23+
24+
assertEquals(o.getClass(), Parent.class);
25+
}
26+
27+
public void testPositiveForChild() throws IOException {
28+
Object o = objectMapper.readerFor(Child.class).readValue("{}");
29+
30+
assertEquals(o.getClass(), Child.class);
31+
}
32+
33+
public void testNegativeForParent() throws IOException {
34+
objectMapper.disable(DeserializationFeature.USE_BASE_TYPE_AS_DEFAULT);
35+
36+
try {
37+
Object o = objectMapper.readerFor(Parent.class).readValue("{}");
38+
} catch (JsonMappingException ex) {
39+
assertTrue(ex.getMessage().contains("missing type id property '@class'"));
40+
}
41+
}
42+
43+
public void testNegativeForChild() throws IOException {
44+
objectMapper.disable(DeserializationFeature.USE_BASE_TYPE_AS_DEFAULT);
45+
46+
try {
47+
Object o = objectMapper.readerFor(Child.class).readValue("{}");
48+
} catch (JsonMappingException ex) {
49+
assertTrue(ex.getMessage().contains("missing type id property '@class'"));
50+
}
51+
}
52+
53+
public void testNegativeConversionForAbstract() throws IOException {
54+
try {
55+
Object o = objectMapper.readerFor(AbstractParentWithDefault.class).readValue("{}");
56+
} catch (JsonMappingException ex) {
57+
assertTrue(ex.getMessage().contains("missing property '@class'"));
58+
}
59+
}
60+
61+
public void testPositiveWithTypeSpecification() throws IOException {
62+
Object o = objectMapper.readerFor(Parent.class)
63+
.readValue("{\"@class\":\"com.fasterxml.jackson.databind.jsontype.TestBaseTypeAsDefault$Child\"}");
64+
65+
assertEquals(o.getClass(), Child.class);
66+
}
67+
68+
public void testPositiveWithManualDefault() throws IOException {
69+
Object o = objectMapper.readerFor(ChildOfAbstract.class).readValue("{}");
70+
71+
assertEquals(o.getClass(), ChildOfChild.class);
72+
}
73+
74+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
75+
static class Parent {
76+
}
77+
78+
79+
static class Child extends Parent {
80+
}
81+
82+
83+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", defaultImpl = ChildOfChild.class)
84+
static abstract class AbstractParentWithDefault {
85+
}
86+
87+
88+
static class ChildOfAbstract extends AbstractParentWithDefault {
89+
}
90+
91+
static class ChildOfChild extends ChildOfAbstract {
92+
}
93+
}

0 commit comments

Comments
 (0)