Skip to content

Commit 4b6f1c4

Browse files
authored
Fix #4920 (#4984)
1 parent 5990e2b commit 4b6f1c4

File tree

4 files changed

+142
-3
lines changed

4 files changed

+142
-3
lines changed

release-notes/CREDITS-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -1880,3 +1880,8 @@ Gustavo Bazan (@gssbzn)
18801880
* Reported #4908: Deserialization behavior change with @JsonCreator and
18811881
@ConstructorProperties between 2.17 and 2.18
18821882
(2.18.3)
1883+
1884+
Zhen Lin Low (@zhenlin-pay2)
1885+
* Reported, fixed #4920: Creator properties are ignored on abstract types
1886+
when collecting bean properties, breaking AsExternalTypeDeserializer
1887+
(2.18.3)

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ Project: jackson-databind
2929
(reported by Gustavo B)
3030
#4917: `BigDecimal` deserialization issue when using `@JsonCreator`
3131
(reported by @dbachdev)
32+
#4920: Creator properties are ignored on abstract types when collecting
33+
bean properties, breaking AsExternalTypeDeserializer
34+
(reported, fix contributed by Zhen L-L)
3235
#4922: Failing `@JsonMerge` with a custom Map
3336
(reported by @nlisker)
3437
#4932: Conversion of `MissingNode` throws `JsonProcessingException`

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,9 @@ protected void addBeanProps(DeserializationContext ctxt,
511511
BeanDescription beanDesc, BeanDeserializerBuilder builder)
512512
throws JsonMappingException
513513
{
514-
final boolean isConcrete = !beanDesc.getType().isAbstract();
515-
final SettableBeanProperty[] creatorProps = isConcrete
516-
? builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig())
514+
final ValueInstantiator valueInstantiator = builder.getValueInstantiator();
515+
final SettableBeanProperty[] creatorProps = (valueInstantiator != null)
516+
? valueInstantiator.getFromObjectArguments(ctxt.getConfig())
517517
: null;
518518
final boolean hasCreatorProps = (creatorProps != null);
519519

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.fasterxml.jackson.databind.deser.creators;
2+
3+
import java.io.IOException;
4+
5+
import org.junit.jupiter.api.Assertions;
6+
import org.junit.jupiter.api.Test;
7+
8+
import com.fasterxml.jackson.annotation.JsonCreator;
9+
import com.fasterxml.jackson.annotation.JsonProperty;
10+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
11+
import com.fasterxml.jackson.databind.DatabindContext;
12+
import com.fasterxml.jackson.databind.JavaType;
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
15+
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
16+
17+
import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.newJsonMapper;
18+
19+
/**
20+
* Unit test for [databind#4920]: Creator properties are ignored on abstract types when
21+
* collecting bean properties, breaking
22+
* {@link com.fasterxml.jackson.databind.jsontype.impl.AsExternalTypeDeserializer}.
23+
*/
24+
public class BeanDeserializerFactory4920Test
25+
{
26+
interface TypedData {
27+
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", visible = true)
28+
@JsonTypeIdResolver(ValueTypeIdResolver.class)
29+
Value getValue();
30+
31+
String getType();
32+
33+
@JsonCreator
34+
static TypedData immutableOf(@JsonProperty("value") Value value, @JsonProperty("type") String type) {
35+
return new TypedData.Immutable(value, type);
36+
}
37+
38+
final class Immutable implements TypedData {
39+
40+
private final Value value;
41+
private final String type;
42+
43+
public Immutable(Value value, String type) {
44+
this.value = value;
45+
this.type = type;
46+
}
47+
48+
@Override
49+
public Value getValue() {
50+
return value;
51+
}
52+
53+
@Override
54+
public String getType() {
55+
return type;
56+
}
57+
}
58+
59+
final class ValueTypeIdResolver extends TypeIdResolverBase {
60+
@Override
61+
public String idFromValue(Object value) {
62+
throw new UnsupportedOperationException();
63+
}
64+
65+
@Override
66+
public String idFromValueAndType(Object value, Class<?> suggestedType) {
67+
throw new UnsupportedOperationException();
68+
}
69+
70+
@Override
71+
public JsonTypeInfo.Id getMechanism() {
72+
return JsonTypeInfo.Id.CUSTOM;
73+
}
74+
75+
@Override
76+
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
77+
Class<?> type;
78+
try {
79+
type = Class.forName(id);
80+
} catch (ClassNotFoundException e) {
81+
throw new IllegalArgumentException(e);
82+
}
83+
84+
return context.constructType(type);
85+
}
86+
}
87+
}
88+
89+
interface Value {
90+
}
91+
92+
static final class StringValue implements Value {
93+
94+
private final String value;
95+
96+
public StringValue(String value) {
97+
this.value = value;
98+
}
99+
100+
public String getValue() {
101+
return value;
102+
}
103+
}
104+
105+
static final class LongValue implements Value {
106+
107+
private final long value;
108+
109+
public LongValue(long value) {
110+
this.value = value;
111+
}
112+
113+
public long getValue() {
114+
return value;
115+
}
116+
}
117+
118+
@Test
119+
void testDeserializeAbstract() throws Exception {
120+
ObjectMapper objectMapper = newJsonMapper();
121+
122+
//language=JSON
123+
String json = "{ \"value\": \"1234567890\", \"type\": \"" + StringValue.class.getName() + "\" }";
124+
125+
TypedData actual = objectMapper.readValue(json, TypedData.class);
126+
127+
Assertions.assertNotNull(actual);
128+
Assertions.assertInstanceOf(StringValue.class, actual.getValue());
129+
Assertions.assertEquals("1234567890", ((StringValue) actual.getValue()).getValue());
130+
}
131+
}

0 commit comments

Comments
 (0)