swagger-api / swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
http://swagger.io
Apache License 2.0
16.98k stars 6.03k forks source link

[java, spring]: codegen.v3:swagger-codegen-maven-plugin does not create @JsonTypeInfo and @JsonSubTypes annotations if polymorphism is used #10594

Open frochi42 opened 3 years ago

frochi42 commented 3 years ago
Description

We are using polymorphism like the famous Pet/Cat/Dog example. In effect, the problem occurs even if you use the example code exactly as provided (see below in "Swagger generation file"

In order to use these classes correctly as body-input in Rest-class, the base class (Pet) must be annotated with @Json-related annotations like this

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Cat.class, name = "Cat"), @JsonSubTypes.Type(value = Dog.class, name = "Dog")})

The io.swagger.codegen.v3:swagger-codegen-maven-plugin simply doesn't create this annotations.

The interesting thing (which I figured out after several hours of research and Try-and-Error): if I use the (older) io.swagger:swagger-codegen-maven-plugin, it works.

Swagger-codegen version

We tested several versions of io.swagger.codegen.v3:swagger-codegen-maven-plugin, latest was 3.0.19. But also 3.0.3 doesn't do it better.

The latest tested version of io.swagger:swagger-codegen-maven-plugin was 2.4.17. As I said, this one works.

Swagger declaration file content or url
      "Pet": {
      "type": "object",
      "discriminator": "petType",
      "properties": {
        "name": {
          "type": "string"
        },
        "petType": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "petType"
      ]
    },
    "Cat": {
      "description": "A representation of a cat",
      "allOf": [
        {
          "$ref": "#/definitions/Pet"
        },
        {
          "type": "object",
          "properties": {
            "huntingSkill": {
              "type": "string",
              "description": "The measured skill for hunting",
              "default": "lazy",
              "enum": [
                "clueless",
                "lazy",
                "adventurous",
                "aggressive"
              ]
            }
          },
          "required": [
            "huntingSkill"
          ]
        }
      ]
    },
    "Dog": {
      "description": "A representation of a dog",
      "allOf": [
        {
          "$ref": "#/definitions/Pet"
        },
        {
          "type": "object",
          "properties": {
            "packSize": {
              "type": "integer",
              "format": "int32",
              "description": "the size of the pack the dog is from",
              "default": 0,
              "minimum": 0
            }
          },
          "required": [
            "packSize"
          ]
        }
      ]
    },
Command line used for generation
           <plugin>
                <groupId>io.swagger.codegen.v3</groupId>
                <artifactId>swagger-codegen-maven-plugin</artifactId>
                <version>3.0.19</version>
                <executions>
                    <execution>
                        <id>api</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>
                                ${file.location}
                            </inputSpec>
                            <language>java</language>
                            <configOptions>
                                <library>jersey2</library>
                                <interfaceOnly>true</interfaceOnly>
                                <useTags>true</useTags>
                                <dateLibrary>java8</dateLibrary>
                            </configOptions>
                            <apiPackage>${default.package}</apiPackage>
                            <modelPackage>${default.package}.model</modelPackage>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Note that for \ I also tested "spring". For \, I tested several values given with '\true\'

Steps to reproduce

call mvn generate and see the result Pet.java class

Related issues/PRs
Suggest a fix/enhancement
dtoch commented 3 years ago

Had the same issue when trying to generate server-side code for language = spring. If the issue is only the missing @JsonTypeInfo and @JsonSubTypes annotations, then you can register a Jackson mixin on the Jackson objectMapper. For example if using Spring Boot, you can do the following :

import io.swagger.model.Animal;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.build().addMixIn(Animal.class, MixinForBug10594.class);
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "typeDiscriminator", visible = true )
    @JsonSubTypes({
            @JsonSubTypes.Type(name = "Dog", value = Dog.class),
            @JsonSubTypes.Type(name = "Cat", value = Cat.class)
    })
    public static class MixinForBug10594 { }
}

Above is a workaround but it's a workaround that doesn't require you to manually modify the generated code. If you're using jersey2 with Jackson instead of Spring with Jackson, then of course where you register the mixin as above will be a bit different.

frochi42 commented 3 years ago

Hi, that's a very interesting approach. Will try that. You are totally right, without that you need to provide a custom version of Animal.class, which is especially dangerous if future changes in the swagger declaration file would happen. Thanks a lot!