java - Swagger 根据枚举值生成子类型
问题描述
我有以下结构
Notification
|
------------------------
| |
SmsNotification EmailNotification
包含一个包含或的Notification
枚举。现在我有一个类,其中包含一个.notificationType
SMS
EMAIL
Inbox
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 {
...
}
这里的问题是,如果我现在尝试反序列化/序列化包含带有 的收件箱的 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
需要生成注释而不是当前行为?
解决方案
我有一个类似的问题并修复了它。
我不直接维护 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>