Skip to content

Commit 91782dc

Browse files
authored
Fix #2541 (support merge polymorphic property) (#3371)
1 parent 3942c2e commit 91782dc

File tree

2 files changed

+91
-5
lines changed

2 files changed

+91
-5
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,12 +561,16 @@ public final Object deserializeWith(JsonParser p, DeserializationContext ctxt,
561561
}
562562
return _nullProvider.getNullValue(ctxt);
563563
}
564-
// 20-Oct-2016, tatu: Also tricky -- for now, report an error
565564
if (_valueTypeDeserializer != null) {
566-
ctxt.reportBadDefinition(getType(),
567-
String.format("Cannot merge polymorphic property '%s'",
568-
getName()));
569-
// return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
565+
// 25-Oct-2021 Added by James to support merging polymorphic property
566+
// https://github.com/FasterXML/jackson-databind/issues/2541
567+
// Please note we only support merging same type polymorphic property for now,
568+
// merging different type is hard and usually doesn't make sense.
569+
// Please note you need to configure {@link DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES} as false to
570+
// enable this feature otherwise the unknown property exception will be thrown.
571+
JavaType subType = ctxt.getTypeFactory().constructType(toUpdate.getClass());
572+
JsonDeserializer<Object> subTypeValueDeserializer = ctxt.findContextualValueDeserializer(subType, this);
573+
return subTypeValueDeserializer.deserialize(p, ctxt, toUpdate);
570574
}
571575
// 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null
572576
Object value = _valueDeserializer.deserialize(p, ctxt, toUpdate);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.fasterxml.jackson.databind.deser;
2+
3+
import com.fasterxml.jackson.annotation.JsonMerge;
4+
import com.fasterxml.jackson.annotation.JsonSubTypes;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.core.JsonProcessingException;
7+
import com.fasterxml.jackson.databind.BaseMapTest;
8+
import com.fasterxml.jackson.databind.DeserializationFeature;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
11+
public class MergePolymorphicTest extends BaseMapTest {
12+
13+
static class Root {
14+
@JsonMerge
15+
public Child child;
16+
}
17+
18+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
19+
@JsonSubTypes({
20+
@JsonSubTypes.Type(value = ChildA.class, name = "ChildA"),
21+
@JsonSubTypes.Type(value = ChildB.class, name = "ChildB")
22+
})
23+
static abstract class Child {
24+
}
25+
26+
static class ChildA extends Child {
27+
public String name;
28+
}
29+
30+
static class ChildB extends Child {
31+
public String code;
32+
}
33+
34+
public void testPolymorphicNewObject() throws JsonProcessingException {
35+
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
36+
Root root = mapper.readValue("{\"child\": { \"@type\": \"ChildA\", \"name\": \"I'm child A\" }}", Root.class);
37+
assertTrue(root.child instanceof ChildA);
38+
assertEquals("I'm child A", ((ChildA) root.child).name);
39+
}
40+
41+
public void testPolymorphicFromNullToNewObject() throws JsonProcessingException {
42+
Root root = new Root();
43+
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
44+
mapper.readerForUpdating(root).readValue("{\"child\": { \"@type\": \"ChildA\", \"name\": \"I'm the new name\" }}");
45+
assertTrue(root.child instanceof ChildA);
46+
assertEquals("I'm the new name", ((ChildA) root.child).name);
47+
}
48+
49+
public void testPolymorphicFromObjectToNull() throws JsonProcessingException {
50+
Root root = new Root();
51+
ChildA childA = new ChildA();
52+
childA.name = "I'm child A";
53+
root.child = childA;
54+
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
55+
mapper.readerForUpdating(root).readValue("{\"child\": null }");
56+
assertTrue(root.child == null);
57+
}
58+
59+
public void testPolymorphicPropertyCanBeMerged() throws JsonProcessingException {
60+
Root root = new Root();
61+
ChildA childA = new ChildA();
62+
childA.name = "I'm child A";
63+
root.child = childA;
64+
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
65+
mapper.readerForUpdating(root).readValue("{\"child\": { \"@type\": \"ChildA\", \"name\": \"I'm the new name\" }}");
66+
assertTrue(root.child instanceof ChildA);
67+
assertEquals("I'm the new name", ((ChildA) root.child).name);
68+
}
69+
70+
public void testPolymorphicPropertyTypeCanNotBeChanged() throws JsonProcessingException {
71+
Root root = new Root();
72+
ChildA childA = new ChildA();
73+
childA.name = "I'm child A";
74+
root.child = childA;
75+
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
76+
mapper.readerForUpdating(root).readValue("{\"child\": { \"@type\": \"ChildB\", \"code\": \"I'm the code\" }}");
77+
// The polymorphic type can't be changed
78+
assertTrue(root.child instanceof ChildA);
79+
assertEquals("I'm child A", ((ChildA) root.child).name);
80+
}
81+
82+
}

0 commit comments

Comments
 (0)