Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swagger2 polymorphisme: add list of abstract classes to swagger documentation

I have a spring-boot application with swagger2. I want to be able to map a list of parent objects to my request model in swagger. I am using annotations atm but using a yaml file is also possible.

Say I have an abstract class Person and two child classes Child and Adult. In my request I have a list of Person's which can contain Child objects and Adult objects.

@JsonTypeInfo(
      use = JsonTypeInfo.Id.NAME,
      include = JsonTypeInfo.As.PROPERTY,
      property = "type",
      visible = true)
@JsonSubTypes({
      @JsonSubTypes.Type(value = Child.class, name = "CHILD"),
      @JsonSubTypes.Type(value = Adult.class, name = "ADULT")})
@ApiModel(value = "Child", subTypes = {Child.class, Adult.class}, discriminator = "type")
public abstract class Person { 
   @ApiModelProperty(notes = "Name of the person", example = "aaron")
   private String name;
   @ApiModelProperty(notes = "Birthdate of the person", example = "2000-07-10")
   private Date birthDate;
   @ApiModelProperty(notes = "Type of the person ('CHILD' or 'ADULT')", example = "CHILD")
   private String type;

   Child(String name, LocalDate birthdate) {
    this.name = name;
    this.birthdate = birthdate;
   }

   Child() {
   }
}

public class Adult extends Person { 
   private String job;

   public Adult(String name, Date birthdate, String job) {
      super(name, birthdate);
      this.job = job;
   }

   Adult() {
   }
}

public class Child extends Person { 
   private List<String> toys;

   public Child(String name, Date birthdate, List<String> toys) {
      super(name, birthdate);
      this.toys = toys;
   }

   Child() {
   }
}

My request object looks like:

public class PersonRequest {

@ApiModelProperty(notes = "Year of insert", example = "2019")
private Integer year;

@ApiModelProperty(notes = "Month of insert", example = "1")
private Integer month;

@ApiModelProperty(notes = "List of persons")
private List<Person> persons;

public SimulationRequest(Integer year, Integer month, List<Person> persons) {
    this.year = year;
    this.month = month;
    this.persons = persons;
}

private SimulationRequest() {
}

public Integer getYear() {
    return year;
}

public Integer getMonth() {
    return month;
}

public List<Person> getPersons() {
    return persons;
}
}

I am not able to get swagger-ui to show a correct model, what I'm getting now is:

PersonRequest {
  persons (Array[Person], optional): List of persons ,
  month (integer, optional): Month of insert ,
  year (integer, optional): Year of insert
}Person {
 name (string, optional): Birthdate of the person ,
 birthDate (string, optional): Name of the person ,
 type (string, optional): Type of the person ('CHILD' or 'ADULT')
}

What I want is something like:

PersonRequest {
  persons (Array[Person], optional): List of persons ,
  month (integer, optional): Month of insert ,
  year (integer, optional): Year of insert
}Child {
 name (string, optional): Birthdate of the person ,
 birthDate (string, optional): Name of the person ,
 type (string, optional): Type of the person ('CHILD' or 'ADULT')
 toys (Array[string], optional): Toys of the child
}Adult {
 name (string, optional): Birthdate of the person ,
 birthDate (string, optional): Name of the person ,
 type (string, optional): Type of the person ('CHILD' or 'ADULT')
 job (string, optional): Job of the adult
}

and with an example value

{
 "persons": [
  {
     "birthdate": "2000-07-10",
     "name": "aaron",
     "type": "CHILD",
     "toys" : ["ball","lego"]
  },
  {
     "birthdate": "1990-07-10",
     "name": "sofia",
     "type": "ADULT",
     "job" : "developer"
  }
],
"month": 6,
"year": 2019
}

I have searched in the documentation but don't seem to find the correct answer to my problem. I've looked at this thread and followed the petstore example in the swagger editor. But I don't seem to find how I can use a list/array of abstract classes in Swagger.

Does anyone have an idea how to do this?

Thanks!

like image 289
RudyVerboven Avatar asked Apr 12 '17 09:04

RudyVerboven


1 Answers

Here is my sample, using Lombok and Swagger 3 annotations. The trick is to use "oneOf" or "anyOf" on the collation with abstract type. See https://swagger.io/docs/specification/describing-request-body/ for details.

@Data
public class PersonRequest {

@Schema(description = "Either Child or Adult",
           anyOf = {Child .class, Adult.class})
private List<? extends Person> persons;

}

public enum PersonType {
        PARENT, CHILD
    }

@Data
public abstract class Person { 
   
   @Schema(notes = "Name of the person", example = "aaron")
   private String name;
   
   @Schema(notes = "Birthdate of the person", example = "2000-07-10")
   @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
   private Date birthDate;
   
   @Schema(notes = "Type of the person ('CHILD' or 'ADULT')", example = "CHILD")
   private PersonType type; //this should be one of the enum values

   Child(String name, LocalDate birthdate) {
    this.name = name;
    this.birthdate = birthdate;
   }

   Child() {
   }
}

@Data
@EqualsAndHashCode(callSuper = true)
public class Adult extends Person { 
   private String job;

   public Adult(String name, Date birthdate, String job) {
      super(name, birthdate);
      this.job = job;
   }

   Adult() {
   }
}

@Data
@EqualsAndHashCode(callSuper = true)
public class Child extends Person { 
   private List<String> toys;

   public Child(String name, Date birthdate, List<String> toys) {
      super(name, birthdate);
      this.toys = toys;
   }

   Child() {
   }
}
like image 174
glory1 Avatar answered Nov 14 '22 23:11

glory1