Skip to content

Commit ee96ce0

Browse files
committed
Merge branch '2.9'
2 parents 9c6a56c + 2e2f4e1 commit ee96ce0

File tree

10 files changed

+246
-27
lines changed

10 files changed

+246
-27
lines changed

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Project: jackson-databind
66

77
2.9.6 (not yet released)
88

9+
#955: Add `MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL` to use declared base type
10+
as `defaultImpl` for polymorphic deserialization
11+
(contributed by mikeldpl@github)
912
#1565: Deserialization failure with Polymorphism using JsonTypeInfo `defaultImpl`,
1013
subtype as target
1114
#1964: Failed to specialize `Map` type during serialization where key type

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,20 @@ public enum MapperFeature implements ConfigFeature
200200
*/
201201
INFER_BUILDER_TYPE_BINDINGS(false),
202202

203+
/**
204+
* Feature that specifies whether the declared base type of a polymorphic value
205+
* is to be used as the "default" implementation, if no explicit default class
206+
* is specified via {@code @JsonTypeInfo.defaultImpl} annotation.
207+
*<p>
208+
* Note that feature only has effect on deserialization of regular polymorphic properties:
209+
* it does NOT affect non-polymorphic cases, and is unlikely to work with Default Typing.
210+
*<p>
211+
* Feature is disabled by default for backwards compatibility.
212+
*
213+
* @since 2.9.6
214+
*/
215+
USE_BASE_TYPE_AS_DEFAULT_IMPL(false),
216+
203217
/*
204218
/******************************************************
205219
/* View-related features

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.reflect.Type;
55
import java.net.URL;
66
import java.text.DateFormat;
7+
import java.util.Collection;
78
import java.util.concurrent.ConcurrentHashMap;
89
import java.util.concurrent.atomic.AtomicReference;
910

@@ -519,7 +520,6 @@ public JavaType constructType(Type t) {
519520
return _typeFactory.constructType(t);
520521
}
521522

522-
523523
/*
524524
/**********************************************************************
525525
/* Configuration, accessing features
@@ -562,6 +562,22 @@ public boolean isEnabled(SerializationFeature f) {
562562
return _serializationConfig.isEnabled(f);
563563
}
564564

565+
/*
566+
/**********************************************************************
567+
/* Configuration, accessing module information
568+
/**********************************************************************
569+
*/
570+
571+
/**
572+
* Method that may be used to find out {@link Module}s that were registered
573+
* when creating this mapper (if any).
574+
*
575+
* @since 3.0
576+
*/
577+
public Collection<com.fasterxml.jackson.databind.Module> getRegisteredModules() {
578+
return _savedBuilderState.modules();
579+
}
580+
565581
/*
566582
/**********************************************************************
567583
/* Public API: constructing Parsers that are properly linked

src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilderState.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.fasterxml.jackson.databind.cfg;
22

3+
import java.util.Arrays;
34
import java.util.Collection;
5+
import java.util.Collections;
46

57
import com.fasterxml.jackson.core.PrettyPrinter;
68
import com.fasterxml.jackson.core.TokenStreamFactory;
@@ -167,6 +169,19 @@ private static com.fasterxml.jackson.databind.Module[] _toArray(Collection<?> co
167169
return coll.toArray(new com.fasterxml.jackson.databind.Module[coll.size()]);
168170
}
169171

172+
/*
173+
/**********************************************************************
174+
/* Configuration access by ObjectMapper
175+
/**********************************************************************
176+
*/
177+
178+
public Collection<com.fasterxml.jackson.databind.Module> modules() {
179+
if (_modules == null) {
180+
return Collections.emptyList();
181+
}
182+
return Collections.unmodifiableList(Arrays.asList(_modules));
183+
}
184+
170185
/*
171186
/**********************************************************************
172187
/* JDK deserialization support

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,36 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
144144
}
145145

146146
TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
147-
JavaType defaultImpl;
147+
JavaType defaultImpl = defineDefaultImpl(config, baseType);
148148

149+
// First, method for converting type info to type id:
150+
switch (_includeAs) {
151+
case WRAPPER_ARRAY:
152+
return new AsArrayTypeDeserializer(baseType, idRes,
153+
_typeProperty, _typeIdVisible, defaultImpl);
154+
case PROPERTY:
155+
case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
156+
return new AsPropertyTypeDeserializer(baseType, idRes,
157+
_typeProperty, _typeIdVisible, defaultImpl, _includeAs);
158+
case WRAPPER_OBJECT:
159+
return new AsWrapperTypeDeserializer(baseType, idRes,
160+
_typeProperty, _typeIdVisible, defaultImpl);
161+
case EXTERNAL_PROPERTY:
162+
return new AsExternalTypeDeserializer(baseType, idRes,
163+
_typeProperty, _typeIdVisible, defaultImpl);
164+
}
165+
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
166+
}
167+
168+
protected JavaType defineDefaultImpl(DeserializationConfig config, JavaType baseType) {
169+
JavaType defaultImpl;
149170
if (_defaultImpl == null) {
150-
defaultImpl = null;
171+
//Fis of issue #955
172+
if (config.isEnabled(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL) && !baseType.isAbstract()) {
173+
defaultImpl = baseType;
174+
} else {
175+
defaultImpl = null;
176+
}
151177
} else {
152178
// 20-Mar-2016, tatu: Can finally add a check for type compatibility BUT
153179
// if so, need to add explicit checks for marker types. Not ideal, but
@@ -178,24 +204,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
178204
}
179205
}
180206
}
181-
182-
// First, method for converting type info to type id:
183-
switch (_includeAs) {
184-
case WRAPPER_ARRAY:
185-
return new AsArrayTypeDeserializer(baseType, idRes,
186-
_typeProperty, _typeIdVisible, defaultImpl);
187-
case PROPERTY:
188-
case EXISTING_PROPERTY: // as per [#528] same class as PROPERTY
189-
return new AsPropertyTypeDeserializer(baseType, idRes,
190-
_typeProperty, _typeIdVisible, defaultImpl, _includeAs);
191-
case WRAPPER_OBJECT:
192-
return new AsWrapperTypeDeserializer(baseType, idRes,
193-
_typeProperty, _typeIdVisible, defaultImpl);
194-
case EXTERNAL_PROPERTY:
195-
return new AsExternalTypeDeserializer(baseType, idRes,
196-
_typeProperty, _typeIdVisible, defaultImpl);
197-
}
198-
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
207+
return defaultImpl;
199208
}
200209

201210
/*

src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,13 @@ final static class BeanWithAnon {
8787
public String toString() { return "sub!"; }
8888
};
8989
}
90-
90+
9191
/*
9292
/**********************************************************
93-
/* Unit tests
93+
/* Test methods
9494
/**********************************************************
9595
*/
9696

97-
// Testing [JACKSON-498], partial fix
9897
public void testEmptyCollection() throws Exception
9998
{
10099
ObjectMapper mapper = ObjectMapper.builder()
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+
5+
import com.fasterxml.jackson.databind.BaseMapTest;
6+
import com.fasterxml.jackson.databind.JsonMappingException;
7+
import com.fasterxml.jackson.databind.MapperFeature;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
10+
import java.io.IOException;
11+
12+
public class TestBaseTypeAsDefault extends BaseMapTest
13+
{
14+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
15+
static class Parent {
16+
}
17+
18+
19+
static class Child extends Parent {
20+
}
21+
22+
23+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", defaultImpl = ChildOfChild.class)
24+
static abstract class AbstractParentWithDefault {
25+
}
26+
27+
28+
static class ChildOfAbstract extends AbstractParentWithDefault {
29+
}
30+
31+
static class ChildOfChild extends ChildOfAbstract {
32+
}
33+
34+
/*
35+
/**********************************************************
36+
/* Test methods
37+
/**********************************************************
38+
*/
39+
40+
protected ObjectMapper MAPPER_WITH_BASE = objectMapperBuilder()
41+
.enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL)
42+
.build();
43+
44+
protected ObjectMapper MAPPER_WITHOUT_BASE = objectMapperBuilder()
45+
.disable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL)
46+
.build();
47+
48+
public void testPositiveForParent() throws IOException {
49+
Object o = MAPPER_WITH_BASE.readerFor(Parent.class).readValue("{}");
50+
assertEquals(o.getClass(), Parent.class);
51+
}
52+
53+
public void testPositiveForChild() throws IOException {
54+
Object o = MAPPER_WITH_BASE.readerFor(Child.class).readValue("{}");
55+
assertEquals(o.getClass(), Child.class);
56+
}
57+
58+
public void testNegativeForParent() throws IOException {
59+
try {
60+
/*Object o =*/ MAPPER_WITHOUT_BASE.readerFor(Parent.class).readValue("{}");
61+
fail("Should not pass");
62+
} catch (JsonMappingException ex) {
63+
assertTrue(ex.getMessage().contains("missing type id property '@class'"));
64+
}
65+
}
66+
67+
public void testNegativeForChild() throws IOException {
68+
try {
69+
/*Object o =*/ MAPPER_WITHOUT_BASE.readerFor(Child.class).readValue("{}");
70+
fail("Should not pass");
71+
} catch (JsonMappingException ex) {
72+
assertTrue(ex.getMessage().contains("missing type id property '@class'"));
73+
}
74+
}
75+
76+
public void testConversionForAbstractWithDefault() throws IOException {
77+
// should pass shouldn't it?
78+
Object o = MAPPER_WITH_BASE.readerFor(AbstractParentWithDefault.class).readValue("{}");
79+
assertEquals(o.getClass(), ChildOfChild.class);
80+
}
81+
82+
public void testPositiveWithTypeSpecification() throws IOException {
83+
Object o = MAPPER_WITH_BASE.readerFor(Parent.class)
84+
.readValue("{\"@class\":\""+Child.class.getName()+"\"}");
85+
assertEquals(o.getClass(), Child.class);
86+
}
87+
88+
public void testPositiveWithManualDefault() throws IOException {
89+
Object o = MAPPER_WITH_BASE.readerFor(ChildOfAbstract.class).readValue("{}");
90+
91+
assertEquals(o.getClass(), ChildOfChild.class);
92+
}
93+
}

src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static class StringWrapper {
6868
private StringWrapper(String s, boolean foo) { _value = s; }
6969

7070
@SuppressWarnings("unused")
71-
private static StringWrapper create(String str) {
71+
private static StringWrapper create(String str) {
7272
return new StringWrapper(str, false);
7373
}
7474
}
@@ -77,6 +77,34 @@ abstract static class StringWrapperMixIn {
7777
@JsonCreator static StringWrapper create(String str) { return null; }
7878
}
7979

80+
// [databind#2020]
81+
@JsonIgnoreProperties("size")
82+
abstract class MyPairMixIn8 { // with and without <Long, String>
83+
@JsonCreator
84+
public Pair2020 with(@JsonProperty("value0") Object value0,
85+
@JsonProperty("value1") Object value1)
86+
{
87+
// body does not matter, only signature
88+
return null;
89+
}
90+
}
91+
92+
static class Pair2020 {
93+
final int x, y;
94+
95+
private Pair2020(int x0, int y0) {
96+
x = x0;
97+
y = y0;
98+
}
99+
100+
@JsonCreator
101+
static Pair2020 with(Object x0, Object y0) {
102+
// static Pair2020 with(@JsonProperty("value0") Object x0, @JsonProperty("value1")Object y0) {
103+
return new Pair2020(((Number) x0).intValue(),
104+
((Number) y0).intValue());
105+
}
106+
}
107+
80108
/*
81109
/**********************************************************
82110
/* Unit tests
@@ -108,12 +136,26 @@ public void testForFactoryAndCtor() throws IOException
108136
assertEquals("stringX", result._a);
109137
}
110138

111-
public void testFactoryMixIn() throws IOException
139+
public void testFactoryDelegateMixIn() throws IOException
112140
{
113141
ObjectMapper mapper = ObjectMapper.builder()
114142
.addMixIn(StringWrapper.class, StringWrapperMixIn.class)
115143
.build();
116144
StringWrapper result = mapper.readValue("\"a\"", StringWrapper.class);
117145
assertEquals("a", result._value);
118146
}
147+
148+
// [databind#2020]
149+
/*
150+
public void testFactoryPropertyMixin() throws Exception
151+
{
152+
ObjectMapper objectMapper = new ObjectMapper();
153+
objectMapper.addMixIn(Pair2020.class, MyPairMixIn8.class);
154+
155+
String doc = aposToQuotes( "{'value0' : 456, 'value1' : 789}");
156+
Pair2020 pair2 = objectMapper.readValue(doc, Pair2020.class);
157+
assertEquals(456, pair2.x);
158+
assertEquals(789, pair2.y);
159+
}
160+
*/
119161
}

src/test/java/com/fasterxml/jackson/databind/introspect/TestMixinMerging.java renamed to src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinMerging.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.databind.introspect;
1+
package com.fasterxml.jackson.databind.mixins;
22

33
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
44
import com.fasterxml.jackson.annotation.JsonProperty;

src/test/java/com/fasterxml/jackson/databind/module/SimpleModuleTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ public MySimpleModule(String name, Version version) {
135135
}
136136
}
137137

138+
/**
139+
* Test module that is different from MySimpleModule. Used to test registration
140+
* of multiple modules.
141+
*/
142+
protected static class AnotherSimpleModule extends SimpleModule
143+
{
144+
public AnotherSimpleModule(String name, Version version) {
145+
super(name, version);
146+
}
147+
}
148+
138149
static class TestModule626 extends SimpleModule {
139150
final Class<?> mixin, target;
140151
public TestModule626(Class<?> t, Class<?> m) {
@@ -284,6 +295,23 @@ public void testMultipleModules() throws Exception
284295
assertSame(SimpleEnum.A, result);
285296
}
286297

298+
public void testGetRegisteredModules()
299+
{
300+
MySimpleModule mod1 = new MySimpleModule("test1", Version.unknownVersion());
301+
AnotherSimpleModule mod2 = new AnotherSimpleModule("test2", Version.unknownVersion());
302+
303+
ObjectMapper mapper = objectMapperBuilder()
304+
.addModule(mod1)
305+
.addModule(mod2)
306+
.build();
307+
308+
List<com.fasterxml.jackson.databind.Module> mods = new ArrayList<>(mapper.getRegisteredModules());
309+
assertEquals(2, mods.size());
310+
// Should retain ordering even if not mandated
311+
assertEquals("test1", mods.get(0).getModuleName());
312+
assertEquals("test2", mods.get(1).getModuleName());
313+
}
314+
287315
/*
288316
/**********************************************************
289317
/* Unit tests; other

0 commit comments

Comments
 (0)