Skip to content

Commit 700ddaf

Browse files
committed
android-record: Support deserializing polymorphic members of records (fixes FasterXML#248).
1 parent 9b2fe00 commit 700ddaf

File tree

3 files changed

+132
-2
lines changed

3 files changed

+132
-2
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.fasterxml.jackson.module.androidrecord;
2+
3+
import com.fasterxml.jackson.databind.BeanDescription;
4+
import com.fasterxml.jackson.databind.DeserializationContext;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
6+
import com.fasterxml.jackson.databind.deser.CreatorProperty;
7+
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
8+
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
9+
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
10+
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
11+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
12+
13+
public class AndroidRecordInstantiator extends StdValueInstantiator {
14+
protected AndroidRecordInstantiator(StdValueInstantiator src) {
15+
super(src);
16+
}
17+
18+
public ValueInstantiator createContextual(DeserializationContext ctxt, BeanDescription beanDesc) throws JsonMappingException {
19+
boolean wasChanged = false;
20+
SettableBeanProperty[] newCtorArgs = new SettableBeanProperty[_constructorArguments.length];
21+
for (int i = 0; i < _constructorArguments.length; i++) {
22+
SettableBeanProperty prop = _constructorArguments[i];
23+
if (!prop.hasValueTypeDeserializer()) {
24+
TypeDeserializer typeDeserializer = ctxt.getFactory().findTypeDeserializer(ctxt.getConfig(), prop.getType());
25+
if (typeDeserializer != null) {
26+
prop = CreatorProperty.construct(
27+
prop.getFullName(),
28+
prop.getType(),
29+
prop.getWrapperName(),
30+
typeDeserializer,
31+
prop.getMember().getAllAnnotations(),
32+
(AnnotatedParameter) prop.getMember(),
33+
prop.getCreatorIndex(),
34+
ctxt.getAnnotationIntrospector().findInjectableValue(prop.getMember()),
35+
prop.getMetadata()
36+
);
37+
wasChanged = true;
38+
}
39+
}
40+
newCtorArgs[i] = prop;
41+
}
42+
43+
44+
if (wasChanged) {
45+
AndroidRecordInstantiator newInstantiator = new AndroidRecordInstantiator(this);
46+
newInstantiator._constructorArguments = newCtorArgs;
47+
return newInstantiator;
48+
} else {
49+
return this;
50+
}
51+
}
52+
}

android-record/src/main/java/com/fasterxml/jackson/module/androidrecord/AndroidRecordModule.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,13 @@ && isDesugaredRecordClass(raw)) {
153153
null, null, parameter.getAllAnnotations(), parameter, i, injectable, null);
154154
}
155155

156-
((StdValueInstantiator) defaultInstantiator).configureFromObjectSettings(null, null, null, null,
157-
constructor, properties);
156+
AndroidRecordInstantiator instantiator = new AndroidRecordInstantiator((StdValueInstantiator) defaultInstantiator);
157+
instantiator.configureFromObjectSettings(null, null, null, null, constructor, properties);
158+
158159
ClassUtil.checkAndFixAccess(constructor.getAnnotated(), false);
159160
found = true;
161+
162+
defaultInstantiator = instantiator;
160163
}
161164
}
162165

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.fasterxml.jackson.module.androidrecord;
2+
3+
import com.android.tools.r8.RecordTag;
4+
import com.fasterxml.jackson.annotation.JsonCreator;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.fasterxml.jackson.annotation.JsonSubTypes;
7+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
10+
public class AbstractRecordMemberTest extends BaseMapTest {
11+
12+
static final class RootRecord extends RecordTag {
13+
14+
private final AbstractMember member;
15+
16+
public RootRecord(AbstractMember member) {
17+
this.member = member;
18+
}
19+
20+
public AbstractMember member() {
21+
return member;
22+
}
23+
}
24+
25+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@class")
26+
@JsonSubTypes({
27+
@JsonSubTypes.Type(value = StringMember.class, name = "string"),
28+
@JsonSubTypes.Type(value = IntMember.class, name = "int")
29+
})
30+
static abstract class AbstractMember {
31+
}
32+
33+
static final class StringMember extends AbstractMember {
34+
35+
private final String val;
36+
37+
@JsonCreator
38+
public StringMember(@JsonProperty("val") String val) {
39+
this.val = val;
40+
}
41+
42+
public String getVal() {
43+
return val;
44+
}
45+
}
46+
47+
static final class IntMember extends AbstractMember {
48+
49+
private final int val;
50+
51+
@JsonCreator
52+
public IntMember(@JsonProperty("val") int val) {
53+
this.val = val;
54+
}
55+
56+
public int getVal() {
57+
return val;
58+
}
59+
}
60+
61+
private final ObjectMapper MAPPER = newJsonMapper();
62+
63+
/*
64+
/**********************************************************************
65+
/* https://github.com/FasterXML/jackson-modules-base/issues/248
66+
/**********************************************************************
67+
*/
68+
public void testDeserializeRecordWithAbstractMember() throws Exception {
69+
RootRecord value = MAPPER.readValue("{\"member\":{\"@class\":\"string\",\"val\":\"Hello, abstract member!\"}}",
70+
RootRecord.class);
71+
assertNotNull(value.member());
72+
assertEquals(StringMember.class, value.member().getClass());
73+
assertEquals("Hello, abstract member!", ((StringMember)value.member()).getVal());
74+
}
75+
}

0 commit comments

Comments
 (0)