升级swagger-jaxrs2-jakarta后嵌套@BeanParam在Swagger-UI渲染异常
解决Swagger 2.2.7嵌套@BeanParam参数未拆分为独立查询项的问题
问题根因
Swagger 2.x对@BeanParam的解析逻辑和1.6.x版本差异较大,默认不会递归解析嵌套在@BeanParam对象内部的@QueryParam字段,而是直接把整个嵌套对象识别为一个复杂JSON参数,这就导致原本应该拆成limit、offset两个独立查询项的内容,现在变成了单个JSON输入框。
解决办法
通过自定义ModelConverter强制Swagger递归解析嵌套的@BeanParam字段,把里面的查询参数提取出来:
1. 编写自定义ModelConverter类
这个类负责识别带@BeanParam注解的字段,递归解析其内部的@QueryParam参数:
import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.core.jackson.ModelResolver; import io.swagger.v3.oas.models.media.Schema; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.QueryParam; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.Iterator; public class NestedBeanParamConverter implements ModelConverter { private final ModelConverter delegate; public NestedBeanParamConverter(ModelConverter delegate) { this.delegate = delegate; } @Override public Schema resolve(Type type, ModelConverterContext context, Iterator<ModelConverter> chain) { Schema schema = delegate.resolve(type, context, chain); if (type instanceof Class<?> clazz) { // 遍历当前类的所有字段 for (Field field : clazz.getDeclaredFields()) { // 找到带@BeanParam的字段 if (field.isAnnotationPresent(BeanParam.class)) { Class<?> nestedClass = field.getType(); // 递归遍历嵌套类的字段,提取@QueryParam参数 for (Field nestedField : nestedClass.getDeclaredFields()) { QueryParam queryParam = nestedField.getAnnotation(QueryParam.class); if (queryParam != null) { // 生成嵌套字段对应的Schema Schema nestedSchema = delegate.resolve(nestedField.getType(), context, chain); // 将嵌套字段添加为顶层查询参数 schema.addProperties(queryParam.value(), nestedSchema); // 标记为查询参数,避免被识别为JSON属性 schema.getExtensions().put("x-query-param", Boolean.TRUE); } } } } } return schema; } }
2. 在Spring Boot中注册自定义转换器
因为你用的是Spring Boot 3,对应的Swagger实现是springdoc-openapi,直接把自定义转换器加到ModelResolver里即可:
import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.core.jackson.ModelResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SwaggerConfig { @Bean public ModelResolver customModelResolver(ObjectMapper objectMapper) { ModelResolver resolver = new ModelResolver(objectMapper); // 添加自定义转换器,确保优先执行 resolver.addConverterFirst(new NestedBeanParamConverter(resolver)); return resolver; } }
3. 验证效果
重启应用后打开Swagger-UI,你会发现原本嵌套在Pagination里的limit和offset已经变成独立的查询参数输入项,不再是JSON格式的整体参数了。
额外提醒
- 如果你的
Pagination类里还有@DefaultValue这类注解,可以在自定义转换器里一并处理,保证参数的默认值等元数据能正常显示 - 要是有多层嵌套的
@BeanParam,可以在转换器里加个递归深度控制,防止无限递归
内容的提问来源于stack exchange,提问作者Leonardo Lima
相关产品推荐
相关产品推荐

