Skip to content

Commit f744259

Browse files
feat: infer discriminator mapping
1 parent d126742 commit f744259

File tree

6 files changed

+152
-2
lines changed

6 files changed

+152
-2
lines changed

modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,23 @@
1515
import com.fasterxml.jackson.databind.AnnotationIntrospector;
1616
import com.fasterxml.jackson.databind.BeanDescription;
1717
import com.fasterxml.jackson.databind.JavaType;
18+
import com.fasterxml.jackson.databind.JsonMappingException;
1819
import com.fasterxml.jackson.databind.ObjectMapper;
1920
import com.fasterxml.jackson.databind.PropertyMetadata;
21+
import com.fasterxml.jackson.databind.SerializationConfig;
2022
import com.fasterxml.jackson.databind.SerializationFeature;
2123
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2224
import com.fasterxml.jackson.databind.introspect.Annotated;
2325
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
26+
import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver;
2427
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
2528
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
2629
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
2730
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
2831
import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
2932
import com.fasterxml.jackson.databind.jsontype.NamedType;
33+
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
34+
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
3035
import com.fasterxml.jackson.databind.util.Annotations;
3136
import io.swagger.v3.core.converter.AnnotatedType;
3237
import io.swagger.v3.core.converter.ModelConverter;
@@ -39,6 +44,7 @@
3944
import io.swagger.v3.core.util.ReferenceTypeUtils;
4045
import io.swagger.v3.core.util.PrimitiveType;
4146
import io.swagger.v3.core.util.ReflectionUtils;
47+
import io.swagger.v3.core.util.RefUtils;
4248
import io.swagger.v3.core.util.ValidatorProcessor;
4349
import io.swagger.v3.oas.annotations.Hidden;
4450
import io.swagger.v3.oas.annotations.Parameter;
@@ -2775,6 +2781,37 @@ protected Discriminator resolveDiscriminator(JavaType type, ModelConverterContex
27752781
}
27762782
}
27772783
}
2784+
} else {
2785+
SerializationConfig config = _mapper.getSerializationConfig();
2786+
AnnotationIntrospector introspector = config.getAnnotationIntrospector();
2787+
List<NamedType> subTypes = introspector.findSubtypes(
2788+
AnnotatedClassResolver.resolveWithoutSuperTypes(config, type.getRawClass())
2789+
);
2790+
2791+
if (subTypes != null && !subTypes.isEmpty()) {
2792+
try {
2793+
TypeSerializer serializer = _mapper.getSerializerFactory().createTypeSerializer(
2794+
config, type
2795+
);
2796+
2797+
if (serializer != null) {
2798+
TypeIdResolver resolver = serializer.getTypeIdResolver();
2799+
2800+
for (NamedType subType : subTypes) {
2801+
String schemaName = context.resolve(new AnnotatedType().type(subType.getType())).getName();
2802+
String subTypeName = resolver.idFromValueAndType(null, subType.getType());
2803+
2804+
// Per the specification, there is an implicit map to schemas with the same name
2805+
// We skip writing the mappings that are implied to keep the schema minimal
2806+
if (!subTypeName.equals(schemaName)) {
2807+
discriminator.mapping(subTypeName, RefUtils.constructRef(schemaName));
2808+
}
2809+
}
2810+
}
2811+
} catch (JsonMappingException e) {
2812+
LOGGER.error("Unable to create type serializer", e);
2813+
}
2814+
}
27782815
}
27792816

27802817
return discriminator;

modules/swagger-core/src/test/java/io/swagger/v3/core/converting/CompositionTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.swagger.v3.core.oas.models.composition.AnimalWithSchemaSubtypes;
99
import io.swagger.v3.core.oas.models.composition.Human;
1010
import io.swagger.v3.core.oas.models.composition.ModelWithFieldWithSubTypes;
11+
import io.swagger.v3.core.oas.models.composition.ModelWithDiscriminatorMapping;
1112
import io.swagger.v3.core.util.Json;
1213
import io.swagger.v3.core.util.ResourceUtils;
1314
import io.swagger.v3.oas.models.media.Schema;
@@ -48,6 +49,11 @@ public void createModelWithFieldWithSubTypes() throws IOException {
4849
compareAsJson(ModelWithFieldWithSubTypes.class, "ModelWithFieldWithSubTypes.json");
4950
}
5051

52+
@Test(description = "create a ModelWithDiscriminatorMapping")
53+
public void createModelWithDiscriminatorMapping() throws IOException {
54+
compareAsJson(ModelWithDiscriminatorMapping.class, "ModelWithDiscriminatorMapping.json");
55+
}
56+
5157
private void compareAsJson(Class<?> cls, String fileName) throws IOException {
5258
final Map<String, Schema> schemas = ModelConverters.getInstance().readAll(cls);
5359
Json.prettyPrint(schemas);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.swagger.v3.core.oas.models.composition;
2+
3+
import com.fasterxml.jackson.annotation.JsonSubTypes;
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5+
import com.fasterxml.jackson.annotation.JsonTypeName;
6+
7+
@JsonTypeInfo(use= JsonTypeInfo.Id.SIMPLE_NAME, include = JsonTypeInfo.As.PROPERTY, property="kind")
8+
@JsonSubTypes({
9+
@JsonSubTypes.Type(value = ModelWithDiscriminatorMapping.First.class),
10+
@JsonSubTypes.Type(value = ModelWithDiscriminatorMapping.Second.class, name = "SecondType"),
11+
@JsonSubTypes.Type(value = ModelWithDiscriminatorMapping.Third.class)
12+
})
13+
public class ModelWithDiscriminatorMapping {
14+
15+
public static class First extends ModelWithDiscriminatorMapping {
16+
private String name;
17+
18+
public String getName() { return name; }
19+
20+
public void setName(String name) { this.name = name; }
21+
}
22+
23+
public static class Second extends ModelWithDiscriminatorMapping {
24+
private String value;
25+
26+
public String getValue() { return value; }
27+
28+
public void setValue(String value) { this.value = value; }
29+
}
30+
31+
@JsonTypeName("ThirdType")
32+
public static class Third extends ModelWithDiscriminatorMapping {
33+
private String value;
34+
35+
public String getValue() { return value; }
36+
37+
public void setValue(String value) { this.value = value; }
38+
}
39+
}

modules/swagger-core/src/test/resources/Animal.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
}
1111
},
1212
"discriminator" : {
13-
"propertyName" : "type"
13+
"propertyName" : "type",
14+
"mapping" : {
15+
"human" : "#/components/schemas/Human",
16+
"pet" : "#/components/schemas/Pet"
17+
}
1418
}
1519
},
1620
"Human": {

modules/swagger-core/src/test/resources/AnimalClass.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
}
1111
},
1212
"discriminator" : {
13-
"propertyName" : "type"
13+
"propertyName" : "type",
14+
"mapping" : {
15+
"human" : "#/components/schemas/HumanClass",
16+
"pet" : "#/components/schemas/PetClass"
17+
}
1418
}
1519
},
1620
"HumanClass": {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"First" : {
3+
"type" : "object",
4+
"allOf" : [ {
5+
"$ref" : "#/components/schemas/ModelWithDiscriminatorMapping"
6+
}, {
7+
"type" : "object",
8+
"properties" : {
9+
"name" : {
10+
"type" : "string"
11+
}
12+
}
13+
} ]
14+
},
15+
"ModelWithDiscriminatorMapping" : {
16+
"required" : [ "kind" ],
17+
"type" : "object",
18+
"properties" : {
19+
"kind" : {
20+
"type" : "string"
21+
}
22+
},
23+
"discriminator" : {
24+
"propertyName" : "kind",
25+
"mapping" : {
26+
"SecondType" : "#/components/schemas/Second",
27+
"ThirdType" : "#/components/schemas/Third"
28+
}
29+
}
30+
},
31+
"Second" : {
32+
"type": "object",
33+
"allOf": [
34+
{
35+
"$ref": "#/components/schemas/ModelWithDiscriminatorMapping"
36+
},
37+
{
38+
"type": "object",
39+
"properties": {
40+
"value": {
41+
"type": "string"
42+
}
43+
}
44+
}
45+
]
46+
},
47+
"Third" : {
48+
"type" : "object",
49+
"allOf" : [ {
50+
"$ref" : "#/components/schemas/ModelWithDiscriminatorMapping"
51+
}, {
52+
"type" : "object",
53+
"properties" : {
54+
"value" : {
55+
"type" : "string"
56+
}
57+
}
58+
} ]
59+
}
60+
}

0 commit comments

Comments
 (0)