You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:35:25