Skip to content

io.swagger.jackson.ModelResolver - Composition model when resolving child type #1896

@shaun-willows

Description

@shaun-willows

When the io.swagger.jackson.ModelResolver's resolve method is called with a parent type, the ModelResolver will create a parent model, as well as a child composition model that references that parent model and does not contain the parent properties. However, when called with a child type, the ModelResolver will create a single model containing both the parent and child properties. Consider the example types below:

@JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(value = Sub1Bean.class, name = "sub1")})
@ApiModel(description = "BaseBean", discriminator = "type", subTypes = {Sub1Bean.class})
static class BaseBean {
    public String type;
    public int a;
    public String b;
}
@ApiModel(description = "Sub1Bean", parent = BaseBean.class)
static class Sub1Bean extends BaseBean {
    public int c;
}

If the resolve method is called with the BaseBean class as its type parameter, then the swagger.json will include the following models:

{
  "BaseBean" : {
    "type" : "object",
    "discriminator" : "type",
    "properties" : {
      "type" : {
        "type" : "string"
      },
      "a" : {
        "type" : "integer",
        "format" : "int32"
      },
      "b" : {
        "type" : "string"
      }
    },
    "description" : "BaseBean"
  },
  "Sub1Bean" : {
    "allOf" : [ {
      "$ref" : "#/definitions/BaseBean"
    }, {
      "type" : "object",
      "properties" : {
        "c" : {
          "type" : "integer",
          "format" : "int32"
        }
      },
      "description" : "Sub1Bean"
    } ]
  }
}

However, if the resolve method is called with the Sub1Bean class as its type parameter, then the swagger.json will include a single model as follows:

{
  "Sub1Bean" : {
    "type" : "object",
    "properties" : {
      "type" : {
        "type" : "string"
      },
      "a" : {
        "type" : "integer",
        "format" : "int32"
      },
      "b" : {
        "type" : "string"
      },
      "c" : {
        "type" : "integer",
        "format" : "int32"
      }
    },
    "description" : "Sub1Bean"
  }
}

This inconsistency becomes an issue when io.swagger.jaxrs.Reader creates models for methods in two separate interfaces, one of which returns the parent class, while the other returns the child class. The models for these two methods will be added to a io.swagger.models.Swagger instance. However, as the models for both methods include a Sub1Bean definition, the exact model definition that will appear in the io.swagger.models.Swagger instance (and ultimately the swagger.json) depends on the order in which the methods are evaluated.

I have updated ModelResolver so that, when presented with the child class as its starting point, the resolve method will define the same set of models in the ModelConverterContext that it would define when presented with the parent class as its starting point. It will still return the child model, but this will be a composition model that references the parent model. The resolve method will only define models for the child class in this manner if three conditions are met. The conditions under which the above will occur include the following:

  1. The child type must have an ApiModel annotation specifying its parent type in the parent property of the annotation.
  2. The child type must in fact be a child of the parent type, as determined by the AnnotationIntrospector instance (_intr) in the parent AbstractModelConverter class.
  3. The ApiModel annotation in the parent class must specify a subTypes property containing the child type.

I am currently working on resolving a few edge cases and will submit a pull request when finished.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions