Skip to content

Commit da97396

Browse files
authored
Issue 2761 (#2762)
Implement #2761 (support multiple type names for `@JsonSubtypes.Type`)
1 parent 23747f7 commit da97396

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java

+3
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@ public List<NamedType> findSubtypes(MapperConfig<?> config, Annotated a)
610610
ArrayList<NamedType> result = new ArrayList<NamedType>(types.length);
611611
for (JsonSubTypes.Type type : types) {
612612
result.add(new NamedType(type.value(), type.name()));
613+
for (String name : type.names()) {
614+
result.add(new NamedType(type.value(), name));
615+
}
613616
}
614617
return result;
615618
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.fasterxml.jackson.databind.jsontype;
2+
3+
import com.fasterxml.jackson.annotation.JsonSubTypes;
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5+
import com.fasterxml.jackson.databind.BaseMapTest;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
8+
import org.junit.Assert;
9+
10+
import java.util.List;
11+
12+
13+
/**
14+
* Tests for sub-types id user uses,
15+
* 1. only names in all sub-types
16+
* 2. 'names' for multi-keys while 'name' for singular keys
17+
*
18+
* [ annotations#171 ]
19+
* As per this feature, keys holding similar java object types can be incorporated
20+
* within the same annotation.
21+
* See this <a href="https://github.com/FasterXML/jackson-annotations/issues/171">issue/improvement</a>
22+
*/
23+
public class TestMultipleTypeNames extends BaseMapTest {
24+
25+
// serializer-deserializer
26+
private final ObjectMapper MAPPER = objectMapper();
27+
28+
29+
// common classes
30+
static class MultiTypeName { }
31+
32+
static class A extends MultiTypeName {
33+
private long x;
34+
public long getX() { return x; }
35+
}
36+
37+
static class B extends MultiTypeName {
38+
private float y;
39+
public float getY() { return y; }
40+
}
41+
42+
43+
44+
45+
46+
// data for test 1
47+
static class WrapperForNamesTest {
48+
private List<BaseForNamesTest> base;
49+
public List<BaseForNamesTest> getBase() { return base; }
50+
}
51+
52+
static class BaseForNamesTest {
53+
private String type;
54+
public String getType() { return type; }
55+
56+
@JsonTypeInfo (
57+
use = JsonTypeInfo.Id.NAME,
58+
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
59+
property = "type"
60+
)
61+
@JsonSubTypes (value = {
62+
@JsonSubTypes.Type(value = A.class, names = "a"),
63+
@JsonSubTypes.Type(value = B.class, names = {"b","c"}),
64+
})
65+
private MultiTypeName data;
66+
public MultiTypeName getData() { return data; }
67+
}
68+
69+
public void testOnlyNames () throws Exception {
70+
String json;
71+
WrapperForNamesTest w;
72+
73+
// TC 1 : all KV serialisation
74+
json = "{\"base\": [{\"type\":\"a\", \"data\": {\"x\": 5}}, {\"type\":\"b\", \"data\": {\"y\": 3.1}}, {\"type\":\"c\", \"data\": {\"y\": 33.8}}]}";
75+
w = MAPPER.readValue(json, WrapperForNamesTest.class);
76+
Assert.assertNotNull(w);
77+
Assert.assertEquals(3, w.base.size());
78+
Assert.assertTrue(w.base.get(0).data instanceof A);
79+
Assert.assertEquals(5l, ((A) w.base.get(0).data).x);
80+
Assert.assertTrue(w.base.get(1).data instanceof B);
81+
Assert.assertEquals(3.1f, ((B) w.base.get(1).data).y, 0);
82+
Assert.assertTrue(w.base.get(2).data instanceof B);
83+
Assert.assertEquals(33.8f, ((B) w.base.get(2).data).y, 0);
84+
85+
86+
// TC 2 : incorrect serialisation
87+
json = "{\"data\": [{\"type\":\"a\", \"data\": {\"x\": 2.2}}, {\"type\":\"b\", \"data\": {\"y\": 5.3}}, {\"type\":\"c\", \"data\": {\"y\": 9.8}}]}";
88+
try {
89+
MAPPER.readValue(json, WrapperForNamesTest.class);
90+
Assert.fail("This serialisation should fail 'coz of x being float");
91+
} catch (UnrecognizedPropertyException e) {}
92+
}
93+
94+
95+
96+
97+
98+
// data for test 2
99+
static class WrapperForNameAndNamesTest {
100+
private List<BaseForNameAndNamesTest> base;
101+
public List<BaseForNameAndNamesTest> getBase() { return base; }
102+
}
103+
104+
static class BaseForNameAndNamesTest {
105+
private String type;
106+
public String getType() { return type; }
107+
108+
@JsonTypeInfo (
109+
use = JsonTypeInfo.Id.NAME,
110+
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
111+
property = "type"
112+
)
113+
@JsonSubTypes (value = {
114+
@JsonSubTypes.Type(value = A.class, name = "a"),
115+
@JsonSubTypes.Type(value = B.class, names = {"b","c"}),
116+
})
117+
private MultiTypeName data;
118+
public MultiTypeName getData() { return data; }
119+
}
120+
121+
public void testNameAndNames () throws Exception {
122+
String json;
123+
WrapperForNameAndNamesTest w;
124+
125+
// TC 1 : all KV serialisation
126+
json = "{\"base\": [{\"type\":\"a\", \"data\": {\"x\": 5}}, {\"type\":\"b\", \"data\": {\"y\": 3.1}}, {\"type\":\"c\", \"data\": {\"y\": 33.8}}]}";
127+
w = MAPPER.readValue(json, WrapperForNameAndNamesTest.class);
128+
Assert.assertNotNull(w);
129+
Assert.assertEquals(3, w.base.size());
130+
Assert.assertTrue(w.base.get(0).data instanceof A);
131+
Assert.assertEquals(5l, ((A) w.base.get(0).data).x);
132+
Assert.assertTrue(w.base.get(1).data instanceof B);
133+
Assert.assertEquals(3.1f, ((B) w.base.get(1).data).y, 0);
134+
Assert.assertTrue(w.base.get(2).data instanceof B);
135+
Assert.assertEquals(33.8f, ((B) w.base.get(2).data).y, 0);
136+
137+
138+
// TC 2 : incorrect serialisation
139+
json = "{\"data\": [{\"type\":\"a\", \"data\": {\"x\": 2.2}}, {\"type\":\"b\", \"data\": {\"y\": 5.3}}, {\"type\":\"c\", \"data\": {\"y\": 9.8}}]}";
140+
try {
141+
MAPPER.readValue(json, WrapperForNameAndNamesTest.class);
142+
Assert.fail("This serialisation should fail 'coz of x being float");
143+
} catch (UnrecognizedPropertyException e) {}
144+
}
145+
146+
}

0 commit comments

Comments
 (0)