Search code examples
springdocspringdoc-openapi-uispringdoc-openui

springdoc-openapi how to display an list of object class as a response?


using the springdocs-openapi library (1.6.8).I'm looking to get the following output:

{
   "result":200,
   "data":{
      "raoGroup":[
         {
            "name":"S/MIME RA Operator"
         }
      ]
   },
   "resultMessage":"操作成功"
}

It's useful to have examples for the fields when using @Schema so that you have them filled automatically. This is the code based on what examples are provided.

UserPrincipalDTO.java

import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

public class UserPrincipalDTO {

//    @Schema(name = "raoGroup", implementation = RaoGroupDTO.class)
    @ArraySchema(minItems = 1, arraySchema = @Schema(implementation = RaoGroupDTO.class), uniqueItems = true)
    private List<RaoGroupDTO> raoGroup;

    public List<RaoGroupDTO> getRaoGroup() {
        return raoGroup;
    }

    public void setRaoGroup(List<RaoGroupDTO> raoGroup) {
        this.raoGroup = raoGroup;
    }
}

RaoGroupDTO.java

import io.swagger.v3.oas.annotations.media.Schema;

public class RaoGroupDTO {
    @Schema(name = "name", description = "xxxx")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserController.java

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;

@RestController
@RequestMapping(value = "/uiapi/user")
@Tag(name = "xxxxx", description = "description Info") // swagger
public class UserController {

    @Operation(summary = "user login")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "xxxxx", content = {
                    @Content(
                            mediaType = "application/json",
                            schema = @Schema(ref = "#/components/schemas/MapSchemaXXX")
                    )
            }),
            @ApiResponse(responseCode = "400", description = "data error", content = {
                    @Content()
            })
    })
    @Logging
    @PostMapping("/login")
    public ResponseEntity<Object> login(HttpServletRequest request, @Valid @RequestBody LoginRequest reqModel, BindingResult reqModelResult) {
        //...
    }
}

OpenApiConfig.java

import com.jrsys.gra.dto.user.UserPrincipalDTO;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class OpenApiConfig {
  @Bean
  public OpenAPI initOpenAPI() {
    OpenAPI openApi = new OpenAPI();
    openApi.info(
            new Info()
                    .title("title....")
                    .description("description .........")
                    .version("v0.1")
    );

    MapSchema mapSchema = new MapSchema();
    ResolvedSchema resolvedSchema = ModelConverters.getInstance()
            .resolveAsResolvedSchema(new AnnotatedType(UserPrincipalDTO.class));

    mapSchema.addProperty("result", new NumberSchema());
    mapSchema.addProperty("resultMessage", new StringSchema());
    mapSchema.addProperty("data", resolvedSchema.schema);

    openApi.components(new Components().addSchemas("MapSchemaXXX", mapSchema));

    return openApi;
  }
}

However, in Swagger-UI, it's not adding the RaoGroupDTO field's example. It's showing like

{
   "result":1,
   "data":{
      "raoGroup":[
         "string"
      ]
   },
   "resultMessage":"string"
}

I'd expect to have something like

{
   "result":1,
   "data":{
      "raoGroup":[
         {
            "name":"string"
         }
      ]
   },
   "resultMessage":"string"
}

How is the output listed above possible through springdocs-openapi library?


Solution

  • It is necessary to create ResolvedSchema of raoGroup.

        MapSchema mapSchema = new MapSchema();
        ResolvedSchema resolvedSchema = ModelConverters.getInstance()
            .resolveAsResolvedSchema(new AnnotatedType(UserPrincipalDTO.class));
    
        // add
        ResolvedSchema raoGroupDtoSchema = ModelConverters.getInstance()
            .resolveAsResolvedSchema(new AnnotatedType(RaoGroupDTO.class));
        resolvedSchema.schema
            .addProperty("raoGroup", new ArraySchema().items(raoGroupDtoSchema.schema));
    
        mapSchema.addProperty("result", new NumberSchema());
        mapSchema.addProperty("resultMessage", new StringSchema());
        mapSchema.addProperty("data", resolvedSchema.schema);
    

    And if you want to output example, you need to set example to @Schema.

    public class RaoGroupDTO {
      @Schema(name = "name", description = "xxxx", example = "S/MIME RA Operator")
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    

    enter image description here