Swagger 根据枚举值生成子类型
Swagger generate subtypes based on enum value
我有以下结构
Notification
|
------------------------
| |
SmsNotification EmailNotification
Notification
包含一个枚举 notificationType
,其中包含 SMS
或 EMAIL
。现在我有一个 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.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>
对于遇到相同问题的任何人,对我有用的是将原始插件 (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
我有以下结构
Notification
|
------------------------
| |
SmsNotification EmailNotification
Notification
包含一个枚举 notificationType
,其中包含 SMS
或 EMAIL
。现在我有一个 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.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>
对于遇到相同问题的任何人,对我有用的是将原始插件 (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