5

I've a Java project that uses Spring Boot and Swagger. The project consists of a simple API controller that implements a single endpoint. Invoking the GET method on the endpoint gets all users. Invoking the PUT method on the same endpoint updates a single user:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    @RequestMapping(value = "", method = RequestMethod.GET)
    public List<BaseUser> getUsers(String name) {
        return null;
    }

    @RequestMapping(value = "", method = RequestMethod.PUT)
    public BaseUser updateUser(@RequestBody BaseUser user) {
        return null;
    }
}

The /api/v1/users endpoint is implemented using a BaseUser which has subtypes like EmployeeUser:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = EmployeeUser.class, name = "employee")
})
public abstract class BaseUser {

    private final int id;
    private final String name;

    @JsonCreator
    protected BaseUser(
            @JsonProperty("id") int id,
            @JsonProperty("name") String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
public class EmployeeUser extends BaseUser {
    public EmployeeUser(@JsonProperty("id") int id,
                     @JsonProperty("name") String name) {
        super(id, name);
    }
}

With this structure, when I browse the Swagger UI I see duplicates of these model types (based on the definitions in the OpenAPI spec).

Swagger UI

I expected to see only a single BaseUser and a single EmployeeUser. What is the reason for the duplicates?

Definitions from OpenAPI Spec:

"definitions": {
    "BaseUserReq": {
        "type": "object",
        "discriminator": "type",
        "properties": {
            "id": {
                "type": "integer",
                "format": "int32"
            },
            "name": {
                "type": "string"
            }
        },
        "title": "BaseUserReq"
    },
    "BaseUserRes": {
        "type": "object",
        "discriminator": "type",
        "properties": {
            "id": {
                "type": "integer",
                "format": "int32"
            },
            "name": {
                "type": "string"
            }
        },
        "title": "BaseUserRes"
    },
    "EmployeeUserReq": {
        "title": "EmployeeUser",
        "allOf": [{
            "$ref": "#/definitions/BaseUserReq"
        }, {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32"
                },
                "name": {
                    "type": "string"
                }
            },
            "title": "EmployeeUserReq"
        }]
    },
    "EmployeeUserRes": {
        "title": "EmployeeUser",
        "allOf": [{
            "$ref": "#/definitions/BaseUserRes"
        }, {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "format": "int32"
                },
                "name": {
                    "type": "string"
                }
            },
            "title": "EmployeeUserRes"
        }]
    }
}
deduper
  • 1,944
  • 9
  • 22
dhrm
  • 14,335
  • 34
  • 117
  • 183

3 Answers3

2

I've had a similar and other problems with version 3.0.0., so I still use 2.9.2 in my projects. You can try the following as an alternative.

When changing dependencies (removed springfox-bean-validators, springfox-boot-starter) in your test project to:

<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

... and changing SwaggerConfig (removing missing imports after changed dependencies) to:

@Configuration
@EnableSwagger2
public class SwaggerConfig { }

... the Swagger UI endpoint at http://localhost:8080/swagger-ui.html will return classes you expect:

enter image description here


Also, you can remove spring-core dependecy since it is already packed within the spring-boot-starter-web, maven-compiler-plugin dependency since it is a plugin, and remove duplicated maven compiler source and target definitions that are defined as properties and a second time as a configuration of the maven compiler plugin.

CAPS LOCK
  • 1,960
  • 3
  • 26
  • 41
2

BaseUserReq, BaseUserRes, EmployeeUserReq and EmployeeUserRes are generated because Springfox 3.0 uses Swagger v3 models by default. You can disable it in application.properties.

Springfox 3.0 uses v3 models by default. To workaround it for now, add: springfox.documentation.swagger.v2.use-model-v3=false in your application.properties. from https://github.com/springfox/springfox/issues/3503#issuecomment-684819445

After disabling springfox v3 models I got :

enter image description here

This issue is described here Suffix with number like "_1" is added to duplicated object name

Hi, @pitzcarraldo. Thanks for the report.

The intention for the last changes, that've been merged to master, and became available in the 3.0.0-SNAPSHOT version is to differ model with not symmetric properties. So if model has different properties for serialization and deserialization SpringFox will generate two models. "ModelName" for serialization and "ModelName_1" for deserialization or vise versa. from https://github.com/springfox/springfox/issues/3014#issuecomment-497941639

I fixed it by removing @JsonCreator and @JsonProperty in BaseUser and EmployeeUser constructors

enter image description here

As of springfox 3.0.0 swagger url is http://localhost:8080/swagger-ui/

See github repository https://github.com/ielatif/swagger-test

Commits history for explanations https://github.com/ielatif/swagger-test/commits/master

Issam El-atif
  • 2,366
  • 2
  • 17
  • 22
  • In my case I cannot remove @JsonProperty because the base class is outside of my control. Any other workaround? – awgtek Dec 07 '21 at 00:48
0

Inheritance works for me, but this happens in my project if I add any extra methods to the models (or maybe it's just methods named setXXX, getXXX and isXXX).

If I make my models simple Java beans with just setters and getters and no other logic, it makes a single schema object without Res or Req suffixes.

whistling_marmot
  • 3,561
  • 3
  • 25
  • 39