How to specify a generic type class for Swagger API response
Asked Answered
B

7

28

I have about 40 APIs that have similar base response structure as follows:

{
    "lastAccessed": "2015-30-08:14:21:45T",
    "createdOn": "2015-30-07:09:04:10T",
    "lastModified": "2015-30-08:14:21:45T",
    "isReadOnly": "false",
    "usersAllowed" : ["Tim", "Matt", "Christine"];
    "noOfEntries": 1,
    "object": [
        "ObjectA": {
             //here object A has its own model
         }
    ]
}

So I have a base response class taking a generic of type T as follows:

public class Response<T> {
    @ApiModelProperty(value="Last time accessed")
    private String lastAccessed;
    @ApiModelProperty(value="Time when Created ")
    private String createdOn;
    private String lastModified;
    @ApiModelProperty(value="Created on")
    private boolean isReadOnly;
    @ApiModelProperty(value="Users that has access to the object.")
    private List<String> usersAllowed;
    private int noOfEntries;
    private T object;

    //getters and setters
}

So for the API A, which returns the Object of type with its own fields, I am returning Response as the API response in the controller:

  public class A {
    @ApiModelProperty(value="Name")
    private String name;
    @ApiModelProperty(value="OID")
    private String id;    
    //getters and setters
}    

In the controller: Response data = new Response(); ResponseEntity response = new ResponseEntity<>(data, HttpStatus.OK);

Is there a way in swagger I can specify the model of the response object recursively? For example, I could have the annotation @ApiOperation(response=Response.class) but that would not have the model for A.

Bigotry answered 1/11, 2015 at 5:55 Comment(2)
did my answer solve your problem??Feudal
The suggestion provided by Marvel77 here looks viable for this issueStalag
F
20

I am using swagger 2 and following resolved this problem for me.

Remove 'response' property from both @ApiResponse and @ApiOperation. Then swagger will automatically generate the response class for you for 200 OK from method stubs (irrespective of whether with/without generics in response class).

@ApiOperation(value = "what your operation does")

@ApiResponses(value = { @ApiResponse(code = 200, message = "Success message") })

Update: You can do this simple work around. Just say you want to output Response<MyClass> as response return type. You can do,

  • In controller class, specify an empty private class like this

    private MyResponseClass extends Response<MyClass>{}

  • And for the swagger spec, specify like this,

    @ApiResponse(code = 200, respone=MyResponseClass.class)

Remember that at the moment, swagger doesn't support generics. Above two are just workarounds.

Feudal answered 6/2, 2018 at 12:24 Comment(1)
Thank you. I have changed to swagger 2 and specified: public class CreateAccountAvatarRes extends RequestRes<RequestRes.CreateAccountAvatarStatus, String> {Shewmaker
B
15

I know this is an old post, but for anyone else looking for an answer to this:

This can be done for List, Set, and Map response objects but any other class class with generic types will be ignored. If you are using any of these three, then you specify them in the responseContainer field and your inferred type in the response field.

@ApiResponse(code = 200, responseContainer="List", respone=java.lang.String.class)
Boiling answered 27/9, 2016 at 21:57 Comment(0)
M
9

This question was asked in 2015, but I was looking for the same thing in 2020. :) So, thought it relevant to add what I found here.

I am using springdoc-openapi-ui (1.5.0), which depends upon Swagger JARs, I understand. Since the common response class takes a generic T, the actual API methods must define the expected type. Eg, Response<Account>.

If this is your scenario too, then NOT defining schema attribute of the @ApiResponse -> @Content seems to make Swagger inspect the generic type and include it in the schema. The same thing is applicable for the Swagger annotation @RequestBody -> @Content

Mold answered 30/11, 2020 at 18:58 Comment(0)
M
4

It took me a while to figure this out. It's 2022 already (swagger 3). Not ideal but something like this seems to do the trick for returning generics in API endpoints with OpenAPI. In the controller:

// use the Response class as the return type
@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Response.class)))
public Response<Potato> getPotato(){
     ...
     return Response<Potato>;
}

In the model:

public class Response<T>{
    // use anyOf/allOf for specifying the types of the wrapped in class
    @Schema(anyOf = { Potato.class, Tomato.class })
    T object;

    ...
}
Moulmein answered 30/4, 2022 at 5:48 Comment(0)
D
3

According to this recently more extensive support for generics was added.

Tested to be working with swagger-jaxrs2: 2.2.0 and swagger-annotations: 2.2.0. I also had to remove any @Schema annotation from class definitions appearing in the response type, and no @Content in @ApiResponse annotations, just something like:

@io.swagger.v3.oas.annotations.Operation(summary = "My summary",
        responses = {@ApiResponse(responseCode = "200", description = "Ok", useReturnTypeSchema = true)}
    )

In my case the response type is GenericClass1<GenericClass2<Class3>>.

Darill answered 9/5, 2022 at 13:32 Comment(0)
G
3

[SOLVED] I got the answer and my solution is up and running I am using OpenApi


        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.6.13</version>
        </dependency>

I almost spent 2 days and miraculously got the solution.

ApiResponse<T> is a generic response class used for all responses in our project

public class ApiResponse<T extends Object> {
    @Schema(description = "status of the request failed/success", example = "SUCCESS")
    private String status;

    @Schema(description = "code to represent the status of the response", example = "200")
    private Integer statusCode;

    @Schema(description = "message for the response")
    private String message;

    private T data;

    private Integer totalCount;
    private String name;
    private Object extraData;
}

SOLUTION :- just don't put any @Schema annotation on the generic type field, it will work. For the example of the Generic responses you can add example in the @Schema for the classes which will be used as the Response.

Example:

@Getter
@Setter
@ToString
@Schema(example = "{\n" +
Some JSON example        
}")

public class SomePlanResponseDTO {

and lastly at the @Controller

@Operation(description = "Fetch the plan", responses = {

    })
    @ApiResponses(value = {
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", useReturnTypeSchema = false, description = "plan found"),
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "Subscription plan not found for the current combination of filters", content = {@Content(schema = @Schema(example = "{\n" +
                    "\t\"status\": \"FAILED\",\n" +
                    "\t\"statusCode\": 404,\n" +
                    "\t\"message\": \"not found\"\n" +
                    "}"))}),
            @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Combination of filters in the request is not appropriate", content = {@Content(schema = @Schema(example = "{\n" +
                    "\t\"status\": \"FAILED\",\n" +
                    "\t\"statusCode\": 400,\n" +
                    "\t\"message\": \"failure\"\n" +
                    "}"))})
    }
    )
    @PostMapping(value = "/somePath", consumes = {MediaType.APPLICATION_JSON_VALUE})
    public ApiResponse<SomePlanResponseDTO > getPlans(@RequestBody SomeRequestDTO someRequestDTO) {
}
Glycogen answered 6/3, 2023 at 13:21 Comment(1)
I have BaseResponse<T> extends ResponseEntity<BaseResponseBody<T>> and in my controllers I return BaseResponse<ClassDTO>. But in the swagger it only resolves ClassDTO and not BaseResponseBody. If I put ResponseEntity<BaseResponseBody<ClassDTO>> as return type instead, it works fine. Do you have any solutions for this?Varian
S
1

I used the kotlin and like that

@ApiOperation(
            value = "Login with email",
            response  = test(Response<AccountResponse>())
    )


fun <T : Any> test(t: T): KClass<out T> = t::class
Silicium answered 3/6, 2021 at 3:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.