如何让Swagger以ISO 8601±HH:MM格式展示ZoneOffset字段?
Spring Boot Swagger 中 ZoneOffset 字段的可读性优化
问题描述
开发订单处理Java应用时,Order类包含ZoneOffset类型的timezone字段:
@Data @NoArgsConstructor @AllArgsConstructor public class Order { private ZoneOffset timezone; }
Spring Boot生成的Swagger响应JSON会渲染ZoneOffset的内部嵌套结构,可读性极差:
{ "timezone": { "totalSeconds": 0, "id": "string", "rules": { "fixedOffset": true, "transitions": [ { "offsetBefore": { "totalSeconds": 0, "id": "string" }, "offsetAfter": { "totalSeconds": 0, "id": "string" }, "duration": { "seconds": 0, "nano": 0, "negative": true, "zero": true, "units": [ { "dateBased": true, "timeBased": true, "durationEstimated": true } ] }, "gap": true, "dateTimeBefore": "2023-02-27T18:33:30.403Z", "dateTimeAfter": "2023-02-27T18:33:30.403Z", "instant": "2023-02-27T18:33:30.403Z", "overlap": true } ], "transitionRules": [ { "month": "JANUARY", "timeDefinition": "UTC", "standardOffset": { "totalSeconds": 0, "id": "string" }, "offsetBefore": { "totalSeconds": 0, "id": "string" }, "offsetAfter": { "totalSeconds": 0, "id": "string" }, "dayOfWeek": "MONDAY", "dayOfMonthIndicator": 0, "localTime": { "hour": 0, "minute": 0, "second": 0, "nano": 0 }, "midnightEndOfDay": true } ] } } }
实际API返回的timezone是+01:00这类简洁字符串,但尝试@ApiModelProperty(example = "+01:00")或@ApiParam(defaultValue="+01:00")均无效,且无法替换ZoneOffset类型(用于业务计算)。
解决方案
方法1:自定义Swagger类型转换器(适配Springdoc)
通过实现ModelConverter接口,强制Swagger将ZoneOffset识别为字符串类型并设置示例值:
import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.oas.models.media.Schema; import java.time.ZoneOffset; import java.util.Iterator; import org.springframework.stereotype.Component; @Component public class ZoneOffsetSchemaConverter implements ModelConverter { @Override public Schema<?> resolve(AnnotatedType annotatedType, ModelConverterContext context, Iterator<ModelConverter> chain) { if (ZoneOffset.class.isAssignableFrom(annotatedType.getType())) { Schema<String> schema = new Schema<>(); schema.setType("string"); schema.setExample("+01:00"); return schema; } return chain.hasNext() ? chain.next().resolve(annotatedType, context, chain) : null; } }
添加该组件后,Swagger会将timezone字段渲染为字符串类型,示例值为+01:00,与实际API返回一致。
方法2:字段注解配合Swagger类型声明(通用)
在ZoneOffset字段上直接指定Swagger的类型和示例,同时依赖Jackson默认的序列化逻辑(Spring Boot已内置Java 8时间模块,会将ZoneOffset序列化为字符串):
@Data @NoArgsConstructor @AllArgsConstructor public class Order { // Springfox 用 @ApiModelProperty,Springdoc 用 @Schema @Schema(type = "string", example = "+01:00") private ZoneOffset timezone; }
该方法无需额外配置类,适合简单场景,注意根据Swagger版本选择对应注解(Springfox:@ApiModelProperty,Springdoc:@Schema)。
方法3:自定义Model构建器(适配Springfox)
如果使用旧版Springfox,可通过ModelBuilderPlugin替换ZoneOffset的Swagger模型:
import com.fasterxml.classmate.TypeResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ModelBuilder; import springfox.documentation.schema.Model; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.schema.ModelBuilderPlugin; import springfox.documentation.spi.schema.contexts.ModelContext; import java.time.ZoneOffset; @Configuration public class SwaggerConfig { @Bean public ModelBuilderPlugin zoneOffsetModelBuilderPlugin(TypeResolver typeResolver) { return new ModelBuilderPlugin() { @Override public void apply(ModelContext context) { if (context.getType().getErasedType() == ZoneOffset.class) { Model model = new ModelBuilder(typeResolver) .name("ZoneOffset") .type(String.class) .example("+01:00") .build(); context.getBuilder().addModel(model); } } @Override public boolean supports(DocumentationType documentationType) { return true; } }; } }
内容的提问来源于stack exchange,提问作者Фёдор Надуткин
相关产品推荐
相关产品推荐

