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

如何让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,提问作者Фёдор Надуткин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 09:45:07