Swagger 根据枚举值生成子类型

Swagger generate subtypes based on enum value

我有以下结构

             Notification
                  |
        ------------------------
        |                      |
  SmsNotification         EmailNotification

Notification 包含一个枚举 notificationType,其中包含 SMSEMAIL。现在我有一个 Inbox class,其中包含一个 Notification

这是在 swagger yml 中指定的(删除了一些不相关的代码)

definitions:
  Notification:
    type: "object"
    discriminator: "notificationType"
    properties:
      notificationType:
        type: "string"
        description: "Type of notification"
        enum:
          - "EMAIL"
          - "SMS"

  SmsNotification:
    allOf:
      - $ref: "#/definitions/Notification"
      - type: "object"

  EmailNotification
    allOf:
      - $ref: "#/definitions/Notification"
      - type: "object"

  Inbox:
    type: "object"
    properties:
      notification:
        description: "Latest received notification"
        $ref: "#/definitions/Notification"

我使用 swagger-codegen v2(也尝试过 v3 和 openapi-generator)生成我的代码,配置如下:

<build>
  <plugins>
      <plugin>
          <groupId>io.swagger</groupId>
          <artifactId>swagger-codegen-maven-plugin</artifactId>
          <version>2.3.1</version>
          <executions>
              <execution>
                 <id>notifications</id>
                  <goals>
                      <goal>generate</goal>
                  </goals>
                  <configuration>
                      <inputSpec>${project.basedir}/src/main/notifications/swagger.yaml</inputSpec>
                      <language>java</language>
                      <library>jersey2</library>
                      <generateSupportingFiles>false</generateSupportingFiles>
                      <modelPackage>${generated.package}</modelPackage>
                      <generateApis>false</generateApis>
                      <generateApiDocumentation>false</generateApiDocumentation>
                      <generateModelTests>false</generateModelTests>
                      <generateModelDocumentation>false</generateModelDocumentation>
                  </configuration>
              </execution>
         </executions>
     </plugin>
  </plugins>
</build>

现在 jersey2 库将生成 JsonSubType 注释,如下所示:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.Property, property="notificationType", visible=true)
@JsonSubTypes({
  @JsonSubTypes.Type(value=SmsNotification.class, name="SmsNotification"),
  @JsonSubTypes.Type(value=EmailNotification.class, name="EmailNotification")
})
public class Notification {
  ...
}

这里的问题是,如果我现在尝试 deserialize/serialize 包含带有 notificationType=EMAIL 的收件箱的 Json 字符串,它将抛出异常,因为没有已知的子类型名字 'EMAIL'

序列化器希望 JsonSubType 注释像这样指定: (旁注,这也是生成 swagger yaml 的代码的样子)

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.Property, property="notificationType", visible=true)
@JsonSubTypes({
  @JsonSubTypes.Type(value=SmsNotification.class, name="SMS"),
  @JsonSubTypes.Type(value=EmailNotification.class, name="EMAIL")
})
public class Notification {
  ...
}

有谁知道如何根据需要生成 JsonSubTypes 注释而不是当前行为?

我遇到了类似的问题并已解决。

我不直接维护 OpenAPI 定义,而是在我的 bean 中使用注释。然后生成 OpenAPI 定义(JSON 格式),然后使用 openapi-generator.

生成客户端项目

问题来自缺少 DiscriminatorMapping 注释。


工作示例

Java豆子

Entity.java

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "type",
    visible = true
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = FooEntity.class, name = "FOO"),
    @JsonSubTypes.Type(value = BarEntity.class, name = "BAR")
})
// Without the following annotations I have the same issue than OP
@Schema(
    description = "Entity",
    discriminatorProperty = "type",
    discriminatorMapping = {
        @DiscriminatorMapping(schema = FooEntity.class, value = "FOO"),
        @DiscriminatorMapping(schema = BarEntity.class, value = "BAR")
    }
)
public abstract class Entity {

    @Schema(description = "Entity type", required = true)
    protected final EntityType type;
}

EntityType.java

@Schema(description = "Entity type")
public enum EntityType {
    FOO,
    BAR
}

FooEntity.javaBarEntity.java 的作用相同)。

@Schema(description = "Foo entity")
public class FooEntity extends Entity {

    public FooEntity() {
        super(EntityType.FOO);
    }
}

定义文件

// ...
"schemas": {
    "Entity": {
        "required": [
            "type"
        ],
        "type": "object",
        "properties": {
            "type": {
                "type": "string",
                "enum": [
                    "FOO",
                    "BAR"
                ]
            }
        },
        "description": "Entity",
        "discriminator": {
            "propertyName": "type",
            "mapping": {
                "FOO": "#/components/schemas/FooEntity",
                "BAR": "#/components/schemas/BarEntity"
            }
        }
    },
    "FooEntity": {
        "required": [
            "type"
        ],
        "type": "object",
        "description": "Foo entity",
        "allOf": [
            {
                "$ref": "#/components/schemas/Entity"
            }
        ]
    },
}
// ...

在我的 pom.xml

中使用以下插件生成
<plugin>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-maven-plugin</artifactId>
    <version>2.1.2</version>
    <executions>
        <execution>
            <id>generate-api-definition</id>
            <phase>compile</phase>
            <goals>
                <goal>resolve</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <configurationFilePath>${project.build.directory}/api/WEB-INF/openapi-configuration.json</configurationFilePath> <!-- My configuration file -->
        <outputPath>${project.build.directory}/api/WEB-INF</outputPath>
        <outputFileName>openapi</outputFileName>
        <outputFormat>JSON</outputFormat>
    </configuration>
</plugin>

生成的客户端

//...
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2020-05-07T15:17:15.844882+02:00[Europe/Paris]")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
@JsonSubTypes({
    @JsonSubTypes.Type(value = FooEntity.class, name = "FOO"),
    @JsonSubTypes.Type(value = BarEntity.class, name = "BAR")
})
public class Entity {

    public enum TypeEnum {
        FOO("FOO"),
        BAR("BAR");
        // ...
    }

    public static final String JSON_PROPERTY_TYPE = "type";
    protected TypeEnum type;

    public Entity type(TypeEnum type) {
        this.type = type;
        return this;
    }

    @ApiModelProperty(required = true, value = "Entity type")
    @JsonProperty(JSON_PROPERTY_TYPE)
    @JsonInclude(value = JsonInclude.Include.ALWAYS)

    public TypeEnum getType() {
        return type;
    }


    public void setType(TypeEnum type) {
        this.type = type;
    }
    // ...
}

生成器插件:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>4.3.1</version>
    <executions>
        <execution>
            <id>generate-client</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <!-- https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-maven-plugin -->
                <inputSpec>x/y/z/openapi.json</inputSpec>
                <addCompileSourceRoot>false</addCompileSourceRoot>
                <generatorName>java</generatorName>
                <configOptions>
                    <java8>true</java8>
                    <serializableModel>true</serializableModel>
                    <serializationLibrary>jackson</serializationLibrary>
                    <library>jersey2</library>
                    <dateLibrary>java8</dateLibrary>
                    <!-- ... -->
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

对于遇到相同问题的任何人,对我有用的是将原始插件 (swagger-codegen-maven-plugin) 更改为上面建议的插件 (openapi-generator-maven-plugin)

我在 swagger-codegen 问题中找到了相关答案(并检查它是否有效)https://github.com/swagger-api/swagger-codegen/issues/1253#issuecomment-277279474

There is a vendor extension "x-discriminator-value" that can be used along with the Jackson annotations to specify the discriminator value to be used instead of the object name.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = MySubClassOne.class, name = "my-sub-class-one")})

招摇

definitions: 
  MySubClassOne: 
    x-discriminator-value: my-sub-class-one