Skip to content

Commit b4b2042

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

File tree

6 files changed

+119
-2
lines changed

6 files changed

+119
-2
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@
1717
import com.fasterxml.jackson.databind.JavaType;
1818
import com.fasterxml.jackson.databind.ObjectMapper;
1919
import com.fasterxml.jackson.databind.PropertyMetadata;
20+
import com.fasterxml.jackson.databind.SerializationConfig;
2021
import com.fasterxml.jackson.databind.SerializationFeature;
2122
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2223
import com.fasterxml.jackson.databind.introspect.Annotated;
2324
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
25+
import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver;
2426
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
2527
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
2628
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
2729
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
2830
import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
2931
import com.fasterxml.jackson.databind.jsontype.NamedType;
32+
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
33+
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
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,30 @@ 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+
BeanDescription bean = config.introspect(type);
2793+
TypeResolverBuilder builder = introspector.findTypeResolver(config, bean.getClassInfo(), type);
2794+
TypeSerializer serializer = builder.buildTypeSerializer(config, type, subTypes);
2795+
TypeIdResolver resolver = serializer.getTypeIdResolver();
2796+
2797+
for (NamedType subType : subTypes) {
2798+
String schemaName = context.resolve(new AnnotatedType().type(subType.getType())).getName();
2799+
String subTypeName = resolver.idFromValueAndType(null, subType.getType());
2800+
2801+
// Per the specification, there is an implicit map to schemas with the same name
2802+
// We skip writing the mappings that are implied to keep the schema minimal
2803+
if (!subTypeName.equals(schemaName)) {
2804+
discriminator.mapping(subTypeName, RefUtils.constructRef(schemaName));
2805+
}
2806+
}
2807+
}
27782808
}
27792809

27802810
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 ModelWithFieldWithSubTypes")
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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
6+
@JsonTypeInfo(use= JsonTypeInfo.Id.SIMPLE_NAME, include = JsonTypeInfo.As.PROPERTY, property="kind")
7+
@JsonSubTypes({
8+
@JsonSubTypes.Type(value = ModelWithDiscriminatorMapping.First.class),
9+
@JsonSubTypes.Type(value = ModelWithDiscriminatorMapping.Second.class, name = "second"),
10+
})
11+
public class ModelWithDiscriminatorMapping {
12+
13+
static class First extends ModelWithDiscriminatorMapping {
14+
private String name;
15+
16+
String getName() { return name; }
17+
18+
void setName(String name) { this.name = name; }
19+
}
20+
21+
static class Second extends ModelWithDiscriminatorMapping {
22+
private String value;
23+
24+
String getValue() { return value; }
25+
26+
void setValue(String mountain) { this.value = mountain; }
27+
}
28+
}

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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
"writeOnly" : true
12+
}
13+
}
14+
} ]
15+
},
16+
"ModelWithDiscriminatorMapping" : {
17+
"required" : [ "kind" ],
18+
"type" : "object",
19+
"properties" : {
20+
"kind" : {
21+
"type" : "string"
22+
}
23+
},
24+
"discriminator" : {
25+
"propertyName" : "kind",
26+
"mapping" : {
27+
"second" : "#/components/schemas/Second"
28+
}
29+
}
30+
},
31+
"Second" : {
32+
"type" : "object",
33+
"allOf" : [ {
34+
"$ref" : "#/components/schemas/ModelWithDiscriminatorMapping"
35+
}, {
36+
"type" : "object",
37+
"properties" : {
38+
"value" : {
39+
"type" : "string",
40+
"writeOnly" : true
41+
}
42+
}
43+
} ]
44+
}
45+
}

0 commit comments

Comments
 (0)