- 开发工具
- 代码生成
- API设计
【免费下载链接】swagger-codegen
swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
导读
本篇文章以 swagger-codegen 仓库中 Petstore 示例的MapTest模型(MapTest.md)为完整样本,系统讲解 OpenAPI 定义中的「嵌套 Map」(Map<String, Map<String, String>>)与「Map 值枚举」(Map<String, InnerEnum>)两种复杂类型,是如何被模板驱动引擎翻译成 Java 客户端 POJO 的。读完本文,你将掌握 Map 类属性的生成规则、字段名与@SerializedName注解的映射关系、枚举值内嵌 Map 时的 Gson 适配器实现,以及链式 setter 与putXxxItem方法的设计模式。
一、MapTest 是什么:一个专门用于测试 Map 类型生成的模型
MapTest是 Petstore fake 接口规范中刻意设计的"测试用模型"。它的目的非常纯粹——验证 swagger-codegen 在面对复杂 Map 类型时能否生成正确、可用的客户端代码。它只包含两个属性,恰好覆盖了两种最具代表性的 Map 场景:
| Name | Type | Description | Notes |
|---|---|---|---|
| mapMapOfString | Map<String, Map<String, String>> | [optional] | |
| mapOfEnumString | [Map<String, InnerEnum>](#Map<String, InnerEnum>) | [optional] |
从optional标记可以看出,两个属性都不是必填项,对应源码中字段的初始值均为null。
说明:文档中
mapMapOfString的类型链接指向同目录下的 Map.md,这是一个通用说明页(当前仓库该示例下未提供独立文件),实际类型以属性表内的Map<String, Map<String, String>>为准。
二、从 OpenAPI 源定义看 Map 类型的原始描述
要理解生成的 Java 代码,先看它的"上游"——OpenAPI/Swagger 定义。在 Petstore fake 规范 petstorefake.yaml 中,MapTest模型定义如下:
MapTest: type: object properties: map_map_of_string: type: object additionalProperties: type: object additionalProperties: type: string map_of_enum_string: type: object additionalProperties: type: string enum: - UPPER - lower这里的两个定义要点:
map_map_of_string是"双层 additionalProperties":外层type: object表示这是一个 Map,additionalProperties再声明值类型为object,其内部又有一个additionalProperties: { type: string }。OpenAPI 中这种写法即等价于 Java 的Map<String, Map<String, String>>。map_of_enum_string是"Map 值枚举":additionalProperties的值类型为string,并带有enum: [UPPER, lower],即 Map 的每个值都必须在枚举范围内,等价于Map<String, InnerEnum>。
值得注意的是,yaml 源定义中有一段被注释掉的map_map_of_enum(map of map of enum)定义,注释原文明确说明:"comment out the following (map of map of enum) as many language not yet support this"(许多语言尚不支持此特性,故注释掉)。这是仓库源码层面的真实限制证据——它解释了为什么 MapTest 只保留了两层 Map 与单层枚举 Map,而没有出现"枚举套 Map 套 Map"的更复杂组合。
三、生成产物概览:MapTest.java 的类骨架
swagger-codegen 依据上述 yaml 定义,为 okhttp-gson 客户端生成了 MapTest.java。该文件位于:
samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/model/MapTest.java类声明为public class MapTest,包含两个核心字段:
@SerializedName("map_map_of_string") private Map<String, Map<String, String>> mapMapOfString = null; @SerializedName("map_of_enum_string") private Map<String, InnerEnum> mapOfEnumString = null;生成逻辑非常直观:yaml 中的属性名map_map_of_string下划线风格被转换为驼峰命名的 Java 字段mapMapOfString,同时通过 Gson 的@SerializedName注解保留原始 JSON 字段名,保证序列化/反序列化时与 API 的 JSON 报文完全对齐。
四、属性详解一:mapMapOfString——嵌套 Map 的生成
mapMapOfString的类型为Map<String, Map<String, String>>,即"外层 Map 的每个 value 又是一个 Map"。这是 OpenAPI 双层additionalProperties的直接产物。
围绕该字段,生成器提供了一组配套方法(MapTest.java):
public MapTest mapMapOfString(Map<String, Map<String, String>> mapMapOfString) { this.mapMapOfString = mapMapOfString; return this; } public MapTest putMapMapOfStringItem(String key, Map<String, String> mapMapOfStringItem) { if (this.mapMapOfString == null) { this.mapMapOfString = new HashMap<String, Map<String, String>>(); } this.mapMapOfString.put(key, mapMapOfStringItem); return this; } public Map<String, Map<String, String>> getMapMapOfString() { return mapMapOfString; } public void setMapMapOfString(Map<String, Map<String, String>> mapMapOfString) { this.mapMapOfString = mapMapOfString; }这里有一个值得注意的设计细节:putMapMapOfStringItem方法会在字段为null时自动初始化一个HashMap,然后逐项放入元素并返回this。这种模式避免了客户端在使用前手动判空初始化,让"逐 key 构建 Map"变得流畅安全。
五、属性详解二:mapOfEnumString——Map 与枚举的组合
mapOfEnumString的类型为Map<String, InnerEnum>,每个 value 都是一个枚举常量。它的方法配套(MapTest.java)与嵌套 Map 版本结构一致:
public MapTest mapOfEnumString(Map<String, InnerEnum> mapOfEnumString) { this.mapOfEnumString = mapOfEnumString; return this; } public MapTest putMapOfEnumStringItem(String key, InnerEnum mapOfEnumStringItem) { if (this.mapOfEnumString == null) { this.mapOfEnumString = new HashMap<String, InnerEnum>(); } this.mapOfEnumString.put(key, mapOfEnumStringItem); return this; } public Map<String, InnerEnum> getMapOfEnumString() { return mapOfEnumString; } public void setMapOfEnumString(Map<String, InnerEnum> mapOfEnumString) { this.mapOfEnumString = mapOfEnumString; }使用方式示例:
MapTest mt = new MapTest() .putMapOfEnumStringItem("k1", InnerEnum.UPPER) .putMapOfEnumStringItem("k2", InnerEnum.LOWER);六、InnerEnum:内嵌枚举及其 Gson 适配器
MapTest.md 文档中用<a name="Map<String, InnerEnum>"></a>锚点专门给出了内嵌枚举的取值表:
| Name | Value |
|---|---|
| UPPER | "UPPER" |
| LOWER | "lower" |
注意两个枚举常量大小写不一致(UPPER全大写、lower全小写),这正是为了验证枚举值与 Java 标识符脱钩时的映射能力。生成的枚举定义位于 MapTest.java:
@JsonAdapter(InnerEnum.Adapter.class) public enum InnerEnum { UPPER("UPPER"), LOWER("lower"); private String value; InnerEnum(String value) { this.value = value; } public String getValue() { return value; } @Override public String toString() { return String.valueOf(value); } public static InnerEnum fromValue(String text) { for (InnerEnum b : InnerEnum.values()) { if (String.valueOf(b.value).equals(text)) { return b; } } return null; } public static class Adapter extends TypeAdapter<InnerEnum> { @Override public void write(final JsonWriter jsonWriter, final InnerEnum enumeration) throws IOException { jsonWriter.value(enumeration.getValue()); } @Override public InnerEnum read(final JsonReader jsonReader) throws IOException { String value = jsonReader.nextString(); return InnerEnum.fromValue(String.valueOf(value)); } } }这个枚举实现了完整的 Gson 序列化闭环:
@JsonAdapter(InnerEnum.Adapter.class):注册自定义类型适配器,替代默认的枚举序列化行为,保证写出的 JSON 值是"UPPER"/"lower"字符串而非 Java 枚举名;write():向JsonWriter写入enumeration.getValue(),即原始 API 约定的字符串值;read():读取 JSON 字符串后经fromValue反查枚举实例,遇到未知值时返回null而非抛异常;fromValue():遍历枚举常量逐一比对原始值,提供从字符串到枚举的转换入口。
七、对象协议:equals / hashCode / toString
生成器还为每个模型补齐了 Java 对象协议方法(MapTest.java):
@Override public boolean equals(java.lang.Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MapTest mapTest = (MapTest) o; return Objects.equals(this.mapMapOfString, mapTest.mapMapOfString) && Objects.equals(this.mapOfEnumString, mapTest.mapOfEnumString); } @Override public int hashCode() { return Objects.hash(mapMapOfString, mapOfEnumString); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class MapTest {\n"); sb.append(" mapMapOfString: ").append(toIndentedString(mapMapOfString)).append("\n"); sb.append(" mapOfEnumString: ").append(toIndentedString(mapOfEnumString)).append("\n"); sb.append("}"); return sb.toString(); }equals使用Objects.equals逐字段比较,天然支持null安全;hashCode基于全部字段计算,符合 equals/hashCode 约定;toString通过私有方法toIndentedString将嵌套对象的换行统一缩进 4 个空格,保证多层 Map 打印时可读。
八、该模型的完整使用闭环
综合以上各节,MapTest的完整用法如下:
import io.swagger.client.model.MapTest; import io.swagger.client.model.MapTest.InnerEnum; import java.util.HashMap; import java.util.Map; // 1. 构建嵌套 Map:外层 key -> 内层 Map<String, String> MapTest test = new MapTest(); Map<String, String> inner = new HashMap<String, String>(); inner.put("pet", "dog"); test.putMapMapOfStringItem("category", inner); // 2. 构建枚举值 Map test.putMapOfEnumStringItem("status1", InnerEnum.UPPER); test.putMapOfEnumStringItem("status2", InnerEnum.LOWER); // 3. 读取 Map<String, Map<String, String>> m1 = test.getMapMapOfString(); Map<String, InnerEnum> m2 = test.getMapOfEnumString();当该模型作为请求体或响应体参与 API 调用时,Gson 会依据@SerializedName与@JsonAdapter完成与 JSON 报文的双向转换。
九、同类模型的横向参考
MapTest并不是仓库中唯一的 Map 型模型,它的生成逻辑与以下同类模型共享同一套模板规则,可作为交叉验证的参考:
- AdditionalPropertiesClass.md:额外属性类,演示
additionalProperties基础用法; - EnumArrays.md:数组与枚举的组合;
- EnumTest.md:标准枚举模型,包含字符串、整数、浮点多种枚举类型;
- 对应源码均位于
samples/client/petstore/java/okhttp-gson/src/main/java/io/swagger/client/model/目录。
从源码结构可以推断:swagger-codegen 的 Java 客户端模板(位于 modules/swagger-codegen 模块下)统一处理MapProperty,并根据其additionalProperties是否为带枚举的StringProperty决定生成"普通泛型 Map"还是"Map + 枚举"的组合形态;而MapTest正是这些模板规则的端到端验证样本。
总结
通过MapTest这一个模型,可以完整观察到 swagger-codegen 处理复杂 Map 类型的全链路:OpenAPI 定义中的双层additionalProperties生成Map<String, Map<String, String>>,带枚举的additionalProperties生成Map<String, InnerEnum>,内嵌枚举被翻译为带@JsonAdapter的 Java 枚举,并配套链式 setter、putXxxItem增量构建、以及完整的equals/hashCode/toString实现。对于任何需要在 Java 客户端中承载嵌套 Map 或枚举值 Map 的 API,这份生成样本都是可直接套用的实践范式。
- 开发工具
- 代码生成
- API设计
【免费下载链接】swagger-codegen
swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
相关推荐
swagger-codegen 生成 Java 客户端中的 Map 模型:以 google-api-client 样例 MapTest 为例
swagger codegen 生成 Java 客户端中的 Map 模型:以 google api client 样例 MapTest 为例 导读 MapTes
开发工具代码生成API设计Swagger Codegen 生成的 User 模型详解:以 Java Jersey1 Petstore 客户端为例
Swagger Codegen 生成的 User 模型详解:以 Java Jersey1 Petstore 客户端为例 导读 本文以 swagger codeg
开发工具代码生成API设计swagger-codegen 生成 Java 客户端 Map 模型实战:以 MapTest 为例解析 OpenAPI 嵌套 Map 与枚举 Map 的落地方式
swagger codegen 生成 Java 客户端 Map 模型实战:以 MapTest 为例解析 OpenAPI 嵌套 Map 与枚举 Map 的落地方式
开发工具代码生成API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考