首页 > 解决方案 > Swagger 根据枚举值生成子类型

问题描述

我有以下结构

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

包含一个包含或的Notification枚举。现在我有一个类,其中包含一个.notificationTypeSMSEMAILInboxNotification

这是在 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 {
  ...
}

这里的问题是,如果我现在尝试反序列化/序列化包含带有 的收件箱的 Json 字符串notificationType=EMAIL,它将引发异常,因为没有已知的带有名称的子类型'EMAIL'

seralizer 期望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需要生成注释而不是当前行为?

标签: javaswaggerswagger-codegenopenapi-generator

解决方案


我有一个类似的问题并修复了它。

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

问题来自缺少DiscriminatorMapping注释。


工作示例

Java bean

实体.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;
}

实体类型.java

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

FooEntity.java ( BarEntity.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>

推荐阅读