1616import com .fasterxml .jackson .databind .BeanDescription ;
1717import com .fasterxml .jackson .databind .JavaType ;
1818import com .fasterxml .jackson .databind .JsonNode ;
19+ import com .fasterxml .jackson .databind .JsonMappingException ;
1920import com .fasterxml .jackson .databind .ObjectMapper ;
2021import com .fasterxml .jackson .databind .PropertyMetadata ;
22+ import com .fasterxml .jackson .databind .SerializationConfig ;
2123import com .fasterxml .jackson .databind .SerializationFeature ;
2224import com .fasterxml .jackson .databind .annotation .JsonSerialize ;
2325import com .fasterxml .jackson .databind .introspect .Annotated ;
2830import com .fasterxml .jackson .databind .introspect .BeanPropertyDefinition ;
2931import com .fasterxml .jackson .databind .introspect .POJOPropertyBuilder ;
3032import com .fasterxml .jackson .databind .jsontype .NamedType ;
33+ import com .fasterxml .jackson .databind .jsontype .TypeIdResolver ;
34+ import com .fasterxml .jackson .databind .jsontype .TypeSerializer ;
3135import com .fasterxml .jackson .databind .util .Annotations ;
3236import io .swagger .v3 .core .converter .AnnotatedType ;
3337import io .swagger .v3 .core .converter .ModelConverter ;
@@ -967,7 +971,7 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
967971 model .setDiscriminator (null );
968972 }
969973
970- Discriminator discriminator = resolveDiscriminator (type , context );
974+ Discriminator discriminator = resolveDiscriminator (model , beanDesc , context , annotatedType . getJsonViewAnnotation () );
971975 if (discriminator != null ) {
972976 model .setDiscriminator (discriminator );
973977 }
@@ -2668,22 +2672,63 @@ protected Map<String, Object> resolveExtensions(Annotated a, Annotation[] annota
26682672 return null ;
26692673 }
26702674
2675+ protected TypeSerializer getTypeSerializer (JavaType type ) {
2676+ SerializationConfig config = _mapper .getSerializationConfig ();
2677+
2678+ try {
2679+ return _mapper .getSerializerFactory ().createTypeSerializer (
2680+ config , type
2681+ );
2682+ } catch (JsonMappingException e ) {
2683+ LOGGER .error ("Unable to create type serializer" , e );
2684+ }
2685+
2686+ return null ;
2687+ }
2688+
2689+ protected TypeSerializer getTypeSerializerForDiscriminatorInference (JavaType type ) {
2690+ // longer method would involve AnnotationIntrospector.findTypeResolver(...) but:
2691+ JsonTypeInfo typeInfo = type .getRawClass ().getDeclaredAnnotation (JsonTypeInfo .class );
2692+ if (typeInfo == null || typeInfo .use () == JsonTypeInfo .Id .NONE ) {
2693+ return null ;
2694+ }
2695+
2696+ TypeSerializer serializer = getTypeSerializer (type );
2697+ if (serializer == null ) {
2698+ return null ;
2699+ }
2700+
2701+ JsonTypeInfo .As include = serializer .getTypeInclusion ();
2702+ if (
2703+ !(
2704+ include == JsonTypeInfo .As .PROPERTY
2705+ || include == JsonTypeInfo .As .EXISTING_PROPERTY
2706+ || include == JsonTypeInfo .As .WRAPPER_OBJECT
2707+ )
2708+ ) {
2709+ return null ;
2710+ }
2711+
2712+ return serializer ;
2713+ }
2714+
26712715 protected void resolveDiscriminatorProperty (JavaType type , ModelConverterContext context , Schema model ) {
26722716 // add JsonTypeInfo.property if not member of bean
2673- JsonTypeInfo typeInfo = type .getRawClass ().getDeclaredAnnotation (JsonTypeInfo .class );
2674- if (typeInfo != null ) {
2675- String typeInfoProp = typeInfo .property ();
2676- if (StringUtils .isNotBlank (typeInfoProp )) {
2677- Schema modelToUpdate = model ;
2678- if (StringUtils .isNotBlank (model .get$ref ())) {
2679- modelToUpdate = context .getDefinedModels ().get (model .get$ref ().substring (SCHEMA_COMPONENT_PREFIX ));
2680- }
2681- if (modelToUpdate .getProperties () == null || !modelToUpdate .getProperties ().keySet ().contains (typeInfoProp )) {
2682- Schema discriminatorSchema = openapi31 ? new JsonSchema ().typesItem ("string" ).name (typeInfoProp ) : new StringSchema ().name (typeInfoProp );
2683- modelToUpdate .addProperties (typeInfoProp , discriminatorSchema );
2684- if (modelToUpdate .getRequired () == null || !modelToUpdate .getRequired ().contains (typeInfoProp )) {
2685- modelToUpdate .addRequiredItem (typeInfoProp );
2686- }
2717+ TypeSerializer serializer = getTypeSerializerForDiscriminatorInference (type );
2718+ if (serializer == null ) {
2719+ return ;
2720+ }
2721+ String propertyName = serializer .getPropertyName ();
2722+ if (StringUtils .isNotBlank (propertyName )) {
2723+ Schema modelToUpdate = model ;
2724+ if (StringUtils .isNotBlank (model .get$ref ())) {
2725+ modelToUpdate = context .getDefinedModels ().get (model .get$ref ().substring (SCHEMA_COMPONENT_PREFIX ));
2726+ }
2727+ if (modelToUpdate .getProperties () == null || !modelToUpdate .getProperties ().keySet ().contains (propertyName )) {
2728+ Schema discriminatorSchema = openapi31 ? new JsonSchema ().typesItem ("string" ).name (propertyName ) : new StringSchema ().name (propertyName );
2729+ modelToUpdate .addProperties (propertyName , discriminatorSchema );
2730+ if (modelToUpdate .getRequired () == null || !modelToUpdate .getRequired ().contains (propertyName )) {
2731+ modelToUpdate .addRequiredItem (propertyName );
26872732 }
26882733 }
26892734 }
@@ -2723,22 +2768,31 @@ protected Schema resolveWrapping(JavaType type, ModelConverterContext context, S
27232768 }
27242769
27252770 protected Discriminator resolveDiscriminator (JavaType type , ModelConverterContext context ) {
2771+ return null ;
2772+ }
2773+
2774+ protected Discriminator resolveDiscriminator (Schema model , BeanDescription bean , ModelConverterContext context , JsonView jsonViewAnnotation ) {
2775+ Discriminator overridenDiscriminator = resolveDiscriminator (bean .getType (), context );
2776+ if (overridenDiscriminator != null ) {
2777+ return overridenDiscriminator ;
2778+ }
27262779
2727- io .swagger .v3 .oas .annotations .media .Schema declaredSchemaAnnotation = AnnotationsUtils .getSchemaDeclaredAnnotation (type .getRawClass ());
2780+ io .swagger .v3 .oas .annotations .media .Schema declaredSchemaAnnotation = AnnotationsUtils .getSchemaDeclaredAnnotation (bean . getType () .getRawClass ());
27282781
2729- String disc = (declaredSchemaAnnotation == null ) ? "" : declaredSchemaAnnotation .discriminatorProperty ();
2782+ if (declaredSchemaAnnotation != null ) {
2783+ String propertyName = declaredSchemaAnnotation .discriminatorProperty ();
27302784
2731- if (disc .isEmpty ()) {
2732- // longer method would involve AnnotationIntrospector.findTypeResolver(...) but:
2733- JsonTypeInfo typeInfo = type .getRawClass ().getDeclaredAnnotation (JsonTypeInfo .class );
2734- if (typeInfo != null ) {
2735- disc = typeInfo .property ();
2785+ if (StringUtils .isBlank (propertyName )) {
2786+ TypeSerializer serializer = getTypeSerializerForDiscriminatorInference (bean .getType ());
2787+ if (serializer == null ) {
2788+ return null ;
2789+ }
2790+ propertyName = serializer .getPropertyName ();
27362791 }
2737- }
2738- if (!disc .isEmpty ()) {
2739- Discriminator discriminator = new Discriminator ()
2740- .propertyName (disc );
2741- if (declaredSchemaAnnotation != null ) {
2792+
2793+ if (StringUtils .isNotBlank (propertyName )) {
2794+ Discriminator discriminator = new Discriminator ().propertyName (propertyName );
2795+
27422796 DiscriminatorMapping [] mappings = declaredSchemaAnnotation .discriminatorMapping ();
27432797 if (mappings != null && mappings .length > 0 ) {
27442798 for (DiscriminatorMapping mapping : mappings ) {
@@ -2747,11 +2801,61 @@ protected Discriminator resolveDiscriminator(JavaType type, ModelConverterContex
27472801 }
27482802 }
27492803 }
2804+
2805+ return discriminator ;
27502806 }
2807+ }
27512808
2752- return discriminator ;
2809+ TypeSerializer serializer = getTypeSerializerForDiscriminatorInference (bean .getType ());
2810+ if (serializer == null ) {
2811+ return null ;
27532812 }
2754- return null ;
2813+ String propertyName = serializer .getPropertyName ();
2814+ if (StringUtils .isBlank (propertyName )) {
2815+ return null ;
2816+ }
2817+
2818+ Discriminator discriminator = new Discriminator ().propertyName (propertyName );
2819+
2820+ // Use same approach to finding subtypes as resolveSubtypes, mimicking
2821+ final List <NamedType > subTypes = _intr ().findSubtypes (bean .getClassInfo ());
2822+ if (subTypes == null ) {
2823+ return null ;
2824+ }
2825+ removeSuperClassAndInterfaceSubTypes (subTypes , bean );
2826+
2827+ final Class <?> beanClass = bean .getClassInfo ().getAnnotated ();
2828+ if (!subTypes .isEmpty ()) {
2829+ TypeIdResolver resolver = serializer .getTypeIdResolver ();
2830+
2831+ for (NamedType subType : subTypes ) {
2832+ final Class <?> subtypeType = subType .getType ();
2833+ if (!beanClass .isAssignableFrom (subtypeType )) {
2834+ continue ;
2835+ }
2836+
2837+ String subTypeName = resolver .idFromValueAndType (null , subType .getType ());
2838+
2839+ final Schema subtypeModel = context .resolve (new AnnotatedType ()
2840+ .type (subtypeType )
2841+ .jsonViewAnnotation (jsonViewAnnotation )
2842+ .subtype (true ));
2843+
2844+ if (StringUtils .isBlank (subtypeModel .getName ()) ||
2845+ subtypeModel .getName ().equals (model .getName ())) {
2846+ subtypeModel .setName (_typeNameResolver .nameForType (_mapper .constructType (subtypeType ),
2847+ TypeNameResolver .Options .SKIP_API_MODEL ));
2848+ }
2849+
2850+ // Per the specification, there is an implicit map to schemas with the same name
2851+ // We skip writing the mappings that are implied to keep the schema minimal
2852+ if (!subTypeName .equals (subtypeModel .getName ())) {
2853+ discriminator .mapping (subTypeName , RefUtils .constructRef (subtypeModel .getName ()));
2854+ }
2855+ }
2856+ }
2857+
2858+ return discriminator ;
27552859 }
27562860
27572861 protected XML resolveXml (Annotated a , Annotation [] annotations , io .swagger .v3 .oas .annotations .media .Schema schema ) {
0 commit comments