
本文介绍如何通过自定义 typeadapterfactory 实现 kotlin 中泛型 optional 类型的条件序列化:当 ispresent 为 false 时完全省略字段(即使启用了 serializenulls),从而精准区分“字段不存在”与“字段存在且为 null”。
本文介绍如何通过自定义 typeadapterfactory 实现 kotlin 中泛型 optional 类型的条件序列化:当 ispresent 为 false 时完全省略字段(即使启用了 serializenulls),从而精准区分“字段不存在”与“字段存在且为 null”。
在使用 Gson 序列化 Kotlin 数据类时,原生不支持对“可选性”(presence)与“空值性”(nullability)进行语义分离。例如,val c: Optional<String> 本意是表达“该字段可有可无”,而 val d: String? 则表示“该字段必存在,但值可为空”。若直接使用默认 Gson 行为,Optional(isPresent = false, value = ...) 仍会被序列化为 "c": null(当启用 serializeNulls())或 "c": null(即使未启用,因 Gson 无法感知 isPresent 逻辑),违背设计初衷。
解决此问题的核心在于拦截 Optional
class OptionalTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
val rawType = type.rawType as? Class<*> ?: return null
if (rawType != Optional::class.java) return null
// 获取泛型参数 T 的 TypeAdapter(如 String、Int? 等)
val elementType = getElementType(type.type)
val elementAdapter = gson.getAdapter(TypeToken.get(elementType))
@Suppress("UNCHECKED_CAST")
return object : TypeAdapter<Optional<*>>() {
override fun write(out: JsonWriter, value: Optional<*>?) {
if (value == null || !value.isPresent) {
// 完全跳过字段写入:不调用 out.name(),也不写值
return
}
// 正常序列化 value.value,复用其对应类型的适配器
elementAdapter.write(out, value.value)
}
override fun read(`in`: JsonReader): Optional<*>? {
// 注意:反序列化需额外处理缺失字段(Gson 不原生支持),此处仅提供基础骨架
// 实际项目中建议结合默认值或改用 Moshi/Kotlinx Serialization
throw UnsupportedOperationException("Deserialization of Optional not fully supported in Gson")
}
} as TypeAdapter<T>
}
private fun getElementType(type: Type): Type {
return (type as? ParameterizedType)?.actualTypeArguments?.get(0) ?: Any::class.java
}
}使用方式如下:
val gson = GsonBuilder()
.serializeNulls() // 允许其他字段输出 null(如 d: Int? = null)
.registerTypeAdapterFactory(OptionalTypeAdapterFactory())
.create()
val request = SimpleRequest(
a = 42,
c = Optional(isPresent = true, value = "Hello"),
d = null,
e = Optional(isPresent = true, value = null) // → 序列化为 "e": null
)
val json = gson.toJson(request)
// 输出:{"a":42,"c":"Hello","d":null,"e":null}
// 注意:b 字段未传入,使用默认值 Optional(false, "") → 完全不出现于 JSON 中✅ 关键行为说明:
- 当 Optional.isPresent == false:字段彻底不写入 JSON(无论 serializeNulls() 是否启用);
- 当 Optional.isPresent == true && value != null:正常序列化 value;
- 当 Optional.isPresent == true && value == null:按 serializeNulls() 规则输出 null(符合 Optional<Int?> 场景);
- 其他非-Optional 字段(如 d: Int?)行为不受影响。
⚠️ 重要注意事项:
- 反序列化限制:Gson 无法原生处理“JSON 中缺失字段 → 构造 isPresent = false 的 Optional”。当前 read() 方法抛出异常,是因为 Gson 缺乏对缺失字段的回调机制(GitHub Issue #1005)。生产环境若需双向支持,强烈建议迁移到 Moshi 或 Kotlinx Serialization,二者对 Kotlin 可选性、默认值及泛型支持更完善;
- Kotlin 互操作性:Gson 对 Kotlin 的空安全、默认参数等支持较弱(Issue #1657),复杂场景下易引发意外行为;
- 性能考量:该适配器为泛型实现,避免了为每种 Optional<T> 单独注册,兼顾灵活性与效率。
综上,通过自定义 TypeAdapterFactory,我们以轻量、可控的方式实现了基于运行时值的字段级序列化策略控制,精准达成“可选字段不存在即不出现”的契约,是 Gson 在 Kotlin 项目中弥补类型系统语义鸿沟的有效实践。

















