Spring Java中Swagger 2.0生成Map<String,List<String>>文档报错解决
Map<String, List<String>>的问题 我之前也碰到过类似的泛型嵌套解析坑,Swagger 2.0(搭配SpringFox)对多层泛型的自动解析确实有点局限,不过有几种实用方案能帮你生成符合预期的规范:
方案1:用@ApiModelProperty明确指定完整数据类型
直接在namesMap字段上添加@ApiModelProperty注解,把泛型的完整类路径写清楚,让Swagger能精准识别嵌套结构:
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.util.List; import java.util.Map; @ApiModel(description = "用户资料实体") public class Profile { @ApiModelProperty( value = "键为分类名称、值为对应名称列表的映射", dataType = "java.util.Map<java.lang.String, java.util.List<java.lang.String>>" ) private Map<String, List<String>> namesMap; // 记得添加getter、setter方法 }
这种方法直接给Swagger指明字段的完整泛型类型,大部分场景下都能解决解析失败的问题,生成的Swagger规范会完全符合你想要的格式:
namesMap:
type: object
additionalProperties:
type: array
items:
type: string
方案2:通过Swagger配置添加类型转换规则
如果方案1没生效,大概率是SpringFox对嵌套泛型的解析逻辑有偏差,可以在Docket配置里加alternateTypeRules,把List<String>映射成Swagger更容易识别的字符串数组(Swagger里array和list的规范是兼容的):
import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.schema.AlternateTypeRules; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; import java.util.Map; import java.util.Collections; @Configuration public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.your.package")) // 替换成你的接口包路径 .paths(PathSelectors.any()) .build() .alternateTypeRules( AlternateTypeRules.newRule( typeResolver.resolve(Map.class, String.class, List.class), typeResolver.resolve(Map.class, String.class, String[].class) ) ) .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfo( "你的API标题", "API功能描述", "1.0版本", "服务条款链接", null, "API许可证", "许可证链接", Collections.emptyList() ); } }
这个配置会让Swagger把Map<String, List<String>>解析成值为字符串数组的对象,最终生成的规范和你预期的完全一致。
方案3:自定义Swagger模型解析器(进阶)
如果前两种方案都满足不了你的特殊需求,可以实现自定义的ModelBuilderPlugin手动构建namesMap的Swagger模型。这种方式灵活性最高,但需要对SpringFox的内部机制有一定了解:
import springfox.documentation.schema.Model; import springfox.documentation.schema.ModelBuilder; import springfox.documentation.schema.ModelBuilderPlugin; import springfox.documentation.schema.ModelPropertyBuilder; import springfox.documentation.schema.TypeBuilder; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.schema.contexts.ModelContext; import springfox.documentation.swagger.common.SwaggerPluginSupport; import org.springframework.plugin.core.OrderedPlugin; import org.springframework.stereotype.Component; import java.lang.reflect.Type; import java.util.Map; import java.util.List; @Component public class CustomMapModelBuilder implements ModelBuilderPlugin, OrderedPlugin { @Override public void apply(ModelContext context) { Type modelType = context.getType(); if (modelType.getErasedType().equals(Map.class)) { Type[] typeArgs = modelType.getTypeBindings().getActualTypeArguments(); // 判断泛型参数是否为String和List<String> if (typeArgs.length == 2 && typeArgs[0].getErasedType().equals(String.class) && typeArgs[1].getErasedType().equals(List.class)) { // 手动构建符合要求的模型 Model customModel = new ModelBuilder() .name(context.getName()) .type("object") .additionalProperties(new ModelPropertyBuilder() .type(new TypeBuilder().arrayModel(new TypeBuilder().stringType().build()).build()) .build()) .build(); context.getBuilder().addModel(customModel); } } } @Override public boolean supports(DocumentationType documentationType) { return documentationType == DocumentationType.SWAGGER_2; } @Override public int getOrder() { return SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1; } }
不过这种方式比较复杂,一般前两种方案就能解决问题,除非你有非常特殊的定制需求。
总结一下,优先试试方案1,简单直接;不行再用方案2调整类型解析规则;最后才考虑自定义插件。
内容的提问来源于stack exchange,提问作者Supun Wijerathne

