Skip to content

Commit 509a4aa

Browse files
committed
Fix #3235
1 parent 92517aa commit 509a4aa

File tree

7 files changed

+133
-6
lines changed

7 files changed

+133
-6
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ Project: jackson-databind
6666
#3227: Content `null` handling not working for root values
6767
(reported by João G)
6868
(fix contributed by proost@github)
69+
#3235: `USE_BASE_TYPE_AS_DEFAULT_IMPL` not working with `DefaultTypeResolverBuilder`
70+
(reported, fix contributed by silas.u / sialais@github)
6971
#3238: Add PropertyNamingStrategies.UpperSnakeCaseStrategy (and UPPER_SNAKE_CASE constant)
7072
(requested by Kenneth J)
7173
(contributed by Tanvesh)

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ public DefaultTypeResolverBuilder(DefaultTyping t, PolymorphicTypeValidator ptv)
260260
_subtypeValidator = _requireNonNull(ptv, "Can not pass `null` PolymorphicTypeValidator");
261261
}
262262

263+
// @since 2.13
264+
protected DefaultTypeResolverBuilder(DefaultTypeResolverBuilder base, Class<?> defaultImpl) {
265+
super(base, defaultImpl);
266+
_appliesFor = base._appliesFor;
267+
_subtypeValidator = base._subtypeValidator;
268+
}
269+
263270
// 20-Jan-2020: as per [databind#2599] Objects.requireNonNull() from JDK7 not in all Android so
264271
private static <T> T _requireNonNull(T value, String msg) {
265272
// Replacement for: return Objects.requireNonNull(t, msg);
@@ -277,6 +284,15 @@ public static DefaultTypeResolverBuilder construct(DefaultTyping t,
277284
return new DefaultTypeResolverBuilder(t, ptv);
278285
}
279286

287+
@Override // since 2.13
288+
public DefaultTypeResolverBuilder withDefaultImpl(Class<?> defaultImpl) {
289+
if (_defaultImpl == defaultImpl) {
290+
return this;
291+
}
292+
ClassUtil.verifyMustOverride(DefaultTypeResolverBuilder.class, this, "withDefaultImpl");
293+
return new DefaultTypeResolverBuilder(this, defaultImpl);
294+
}
295+
280296
@Override // since 2.10
281297
public PolymorphicTypeValidator subTypeValidator(MapperConfig<?> config) {
282298
return _subtypeValidator;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,8 +1797,10 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
17971797
// (note: check for abstract type is not 100% mandatory, more of an optimization)
17981798
if ((b.getDefaultImpl() == null) && baseType.isAbstract()) {
17991799
JavaType defaultType = mapAbstractType(config, baseType);
1800+
// 18-Sep-2021, tatu: We have a shared instance, MUST NOT call mutating method
1801+
// but the new "mutant factory":
18001802
if ((defaultType != null) && !defaultType.hasRawClass(baseType.getRawClass())) {
1801-
b = b.defaultImpl(defaultType.getRawClass());
1803+
b = b.withDefaultImpl(defaultType.getRawClass());
18021804
}
18031805
}
18041806
// 05-Apt-2018, tatu: Since we get non-mapping exception due to various limitations,

src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
9797
* but not necessarily)
9898
*/
9999
public T init(JsonTypeInfo.Id idType, TypeIdResolver res);
100-
100+
101101
/*
102102
/**********************************************************
103103
/* Methods for configuring resolver to build
@@ -136,7 +136,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
136136
/**
137137
* Method for specifying default implementation to use if type id
138138
* is either not available, or cannot be resolved.
139-
*
139+
*
140140
* @return Resulting builder instance (usually this builder,
141141
* but may be a newly constructed instance for immutable builders}
142142
*/
@@ -152,4 +152,25 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
152152
* @since 2.0
153153
*/
154154
public T typeIdVisibility(boolean isVisible);
155+
156+
/*
157+
/**********************************************************************
158+
/* Mutant factories (2.13+)
159+
/**********************************************************************
160+
*/
161+
162+
/**
163+
* "Mutant factory" method for creating a new instance with different default
164+
* implementation.
165+
*
166+
* @since 2.13
167+
*
168+
* @return Either this instance (if nothing changed) or a new instance with
169+
* different default implementation
170+
*/
171+
public default T withDefaultImpl(Class<?> defaultImpl) {
172+
// 18-Sep-2021, tatu: Not sure if this should be left failing, or use
173+
// possibly unsafe variant
174+
return defaultImpl(defaultImpl);
175+
}
155176
}

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ protected StdTypeResolverBuilder(JsonTypeInfo.Id idType,
6060
_typeProperty = propName;
6161
}
6262

63+
/**
64+
* Copy-constructor
65+
*
66+
* @since 2.13
67+
*/
68+
protected StdTypeResolverBuilder(StdTypeResolverBuilder base,
69+
Class<?> defaultImpl)
70+
{
71+
_idType = base._idType;
72+
_includeAs = base._includeAs;
73+
_typeProperty = base._typeProperty;
74+
_typeIdVisible = base._typeIdVisible;
75+
_customIdResolver = base._customIdResolver;
76+
77+
_defaultImpl = defaultImpl;
78+
}
79+
6380
public static StdTypeResolverBuilder noTypeInfoBuilder() {
6481
return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null);
6582
}
@@ -243,7 +260,18 @@ public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {
243260
_typeIdVisible = isVisible;
244261
return this;
245262
}
246-
263+
264+
@Override
265+
public StdTypeResolverBuilder withDefaultImpl(Class<?> defaultImpl) {
266+
if (_defaultImpl == defaultImpl) {
267+
return this;
268+
}
269+
ClassUtil.verifyMustOverride(StdTypeResolverBuilder.class, this, "withDefaultImpl");
270+
271+
// NOTE: MUST create new instance, NOT modify this instance
272+
return new StdTypeResolverBuilder(this, defaultImpl);
273+
}
274+
247275
/*
248276
/**********************************************************
249277
/* Accessors

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ static class Parent {
1616
static class Child extends Parent {
1717
}
1818

19-
2019
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", defaultImpl = ChildOfChild.class)
2120
static abstract class AbstractParentWithDefault {
2221
}
2322

24-
2523
static class ChildOfAbstract extends AbstractParentWithDefault {
2624
}
2725

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.fasterxml.jackson.databind.jsontype.deftyping;
2+
3+
import java.util.LinkedList;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.TreeMap;
7+
8+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
9+
10+
import com.fasterxml.jackson.databind.*;
11+
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
12+
import com.fasterxml.jackson.databind.module.SimpleModule;
13+
14+
public class DefaultTypeAbstractMapping3235Test extends BaseMapTest
15+
{
16+
// [databind#3235]
17+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
18+
static class Parent { }
19+
static class Child extends Parent { }
20+
21+
static abstract class AbstractParentWithoutDefault {}
22+
23+
static class ChildOfParentWithoutDefault extends AbstractParentWithoutDefault {
24+
public Map<String,String> mapField;
25+
public Parent objectField;
26+
}
27+
28+
// [databind#3235]
29+
public void testForAbstractTypeMapping() throws Exception
30+
{
31+
// [databind#3235]
32+
ObjectMapper mapper3235 = jsonMapperBuilder()
33+
.enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL)
34+
.addModule(new SimpleModule()
35+
.addAbstractTypeMapping(AbstractParentWithoutDefault.class, ChildOfParentWithoutDefault.class)
36+
.addAbstractTypeMapping(Map.class, TreeMap.class)
37+
.addAbstractTypeMapping(List.class, LinkedList.class)
38+
)
39+
.registerSubtypes(TreeMap.class, LinkedList.class, ChildOfParentWithoutDefault.class)
40+
.setDefaultTyping(
41+
new ObjectMapper.DefaultTypeResolverBuilder(
42+
ObjectMapper.DefaultTyping.NON_FINAL, LaissezFaireSubTypeValidator.instance
43+
).init(JsonTypeInfo.Id.CLASS, null
44+
).inclusion(JsonTypeInfo.As.PROPERTY)
45+
)
46+
.build();
47+
String doc = a2q(
48+
"{" +
49+
" 'mapField': {" +
50+
" 'a':'a'" +
51+
" }, " +
52+
" 'objectField': {}" +
53+
"}");
54+
Object o = mapper3235.readValue(doc, AbstractParentWithoutDefault.class);
55+
assertEquals(o.getClass(), ChildOfParentWithoutDefault.class);
56+
ChildOfParentWithoutDefault ot = (ChildOfParentWithoutDefault) o;
57+
assertEquals(ot.mapField.getClass(), TreeMap.class);
58+
assertEquals(ot.objectField.getClass(), Parent.class);
59+
}
60+
}

0 commit comments

Comments
 (0)