如何通过Java的@Schema注解生成支持null的OpenAPI引用对象?
解决Swagger中通过注解让引用字段支持null值的问题
问题场景
现有Java类结构如下:
public class User { private int age; private Address address; } public class Address { private String city; }
希望address字段支持三种客户端请求场景:完全省略字段、传入空对象"address": {}、传入"address": null,但遇到以下问题:
- 未加任何注解时,生成的Swagger中
address仅包含对Address的$ref,不支持null值 - 给
address字段添加@Schema(nullable=true)时,该配置会被$ref覆盖,无法生效 - 已知直接修改Swagger可以用
anyOf结合type: null和$ref实现需求,但@Schema的anyOf属性仅支持指定类,无法直接生成type: null的规则 - 由于Address类结构复杂且为多API共享,不想直接在Address类上添加
@Schema(nullable=true)
可行解决方案
方案1:自定义ModelConverter扩展(推荐)
swagger-core允许通过自定义ModelConverter来修改生成的OpenAPI Schema,无需修改共享类即可为指定字段添加anyOf包含null的规则。
自定义Converter代码
import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.oas.models.media.Schema; import java.lang.reflect.Type; import java.util.Iterator; public class NullableRefModelConverter implements ModelConverter { @Override public Schema resolve(Type type, ModelConverterContext context, Iterator<ModelConverter> chain) { Schema schema = chain.next().resolve(type, context, chain); // 根据当前字段名和引用目标判断是否需要处理(示例针对User类的address字段) if (schema != null && schema.get$ref() != null) { String refPath = schema.get$ref(); String currentField = context.getCurrentPropertyName(); if (refPath.contains("Address") && "address".equals(currentField)) { // 创建null类型的Schema Schema nullSchema = new Schema(); nullSchema.setType("null"); // 为当前字段设置anyOf,包含null和原引用类型 schema.setAnyOf(java.util.List.of(nullSchema, new Schema().$ref(refPath))); // 移除原$ref,避免冲突 schema.set$ref(null); } } return schema; } }
注册Converter
在应用启动时注册自定义Converter:
import io.swagger.v3.core.converter.ModelConverters; // 启动初始化逻辑中添加 ModelConverters.getInstance().addConverter(new NullableRefModelConverter());
生成的Swagger中address字段会变为:
"address": { "anyOf": [ { "type": "null" }, { "$ref": "#/components/schemas/Address" } ] }
方案2:使用自定义包装类(略繁琐)
创建一个标记了anyOf的包装类:
@Schema(anyOf = {Address.class, Void.class}) public class NullableAddress {}
修改User类的address字段类型为NullableAddress:
public class User { private int age; private NullableAddress address; }
该方案需要修改User类的字段类型,适合字段规则仅在少数场景使用的情况。
方案3:API级别单独配置(局部场景)
如果仅个别API需要该规则,可以在接口方法的@RequestBody中直接指定Schema:
@PostMapping("/users") public ResponseEntity<User> createUser( @RequestBody @Schema( properties = { @SchemaProperty(name = "age", type = "integer"), @SchemaProperty(name = "address", anyOf = { @Schema(type = "null"), @Schema(ref = "#/components/schemas/Address") }) } ) User user ) { // 业务逻辑处理 }
该方式无需修改实体类,但需要在每个目标API上单独配置。
内容的提问来源于stack exchange,提问作者mtv
相关产品推荐
相关产品推荐

