使用GSON序列化Kotlin数据类,基于值跳过泛型字段序列化可行吗?
方案可行性分析及实现要点
你的方案思路是可行的,但必须配合GSON的自定义扩展才能实现期望的序列化效果,默认GSON无法直接达到你要的输出格式,具体分析和实现要点如下:
核心思路的合理性
用Optional<T>封装字段来区分「可选字段(字段可存在/不存在)」和「可空字段(字段必存在但值可为null)」的语义是合理的,能明确区分两种不同的业务场景,避免混淆。
需要解决的关键问题
默认情况下,GSON会把Optional<T>实例序列化成包含isPresent和value的嵌套JSON对象(比如{"isPresent":true,"value":"Hello"}),而不是直接将value作为字段值,同时也不会自动过滤isPresent=false的字段。因此需要做两处自定义扩展:
1. 自定义TypeAdapter:将Optional实例直接序列化为其value值
这个适配器负责把Optional<T>对象在序列化时直接输出内部的value,反序列化时把JSON值还原为isPresent=true的Optional实例:
class OptionalTypeAdapter<T>(private val delegate: TypeAdapter<T>) : TypeAdapter<Optional<T>>() { override fun write(out: JsonWriter, value: Optional<T>?) { if (value?.isPresent == true) { delegate.write(out, value.value) } } override fun read(`in`: JsonReader): Optional<T> { val value = delegate.read(`in`) return Optional(isPresent = true, value = value) } }
2. 自定义ExclusionStrategy:过滤isPresent=false的Optional字段
这个策略会在序列化时跳过所有isPresent为false的Optional字段,实现「可选字段不指定就不出现」的效果:
class OptionalExclusionStrategy : ExclusionStrategy { override fun shouldSkipField(f: FieldAttributes): Boolean { if (Optional::class.java.isAssignableFrom(f.declaredType)) { return try { val field = f.field.apply { isAccessible = true } val optional = field.get(f.owner) as Optional<*> !optional.isPresent } catch (_: Exception) { false } } return false } override fun shouldSkipClass(clazz: Class<*>?): Boolean = false }
3. 配置GSON实例
将上述自定义组件注册到GSON,并开启空值序列化(确保d、e这类可空字段的null值能被输出):
val gson = GsonBuilder() .registerTypeAdapterFactory(object : TypeAdapterFactory { override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { val rawType = type.rawType return if (rawType == Optional::class.java) { val parameterizedType = type.type as ParameterizedType val valueType = parameterizedType.actualTypeArguments[0] val delegateAdapter = gson.getAdapter(TypeToken.get(valueType)) OptionalTypeAdapter(delegateAdapter) as TypeAdapter<T> } else { null } } }) .addSerializationExclusionStrategy(OptionalExclusionStrategy()) .serializeNulls() .create()
验证效果
用你给出的实例测试:
val request = SimpleRequest( c = Optional(isPresent = true, value = "Hello"), e = Optional(isPresent = true, value = null) ) val json = gson.toJson(request)
输出的JSON会和你期望的一致:
{ "a": 0, "c": "Hello", "d": null, "e": null }
注意事项
- 确保
SimpleRequest中Optional类型字段的默认值是isPresent=false的实例,这样未指定的字段会被自动过滤。 - 对于
Optional<Int?>这类嵌套可空的类型,TypeAdapter能正确处理value=null的情况,配合serializeNulls会输出null值。
内容的提问来源于stack exchange,提问作者cradical
相关产品推荐
相关产品推荐

