Retrofit2+Gson自定义ZonedDateTime序列化器失效问题求助
Retrofit2 + Gson 中 ZonedDateTime 自定义序列化器失效问题解决
问题原因
Gson 的 JsonSerializer 仅负责JSON请求体的序列化,并不处理 @Query 这类查询参数的字符串转换。Retrofit 处理查询参数时,默认逻辑是直接调用参数的 toString() 方法,或者使用内置的 StringConverter,不会触发你注册的 Gson 自定义序列化器。
你的 LocalDate 序列化器能生效,大概率是因为 Gson 和 Retrofit 都没有默认处理 LocalDate 的逻辑,因此 fallback 到了你的自定义序列化器;而 ZonedDateTime 存在默认的字符串转换逻辑,所以没走到你的序列化代码。
解决方案
方案1:自定义 StringConverter 专门处理 ZonedDateTime
创建一个 Converter.Factory,直接将 ZonedDateTime 转换为目标格式的字符串,优先于 Gson 处理查询参数:
class ZonedDateTimeConverterFactory : Converter.Factory() { override fun stringConverter( type: Type, annotations: Array<Annotation>, retrofit: Retrofit ): Converter<*, String>? { return if (type == ZonedDateTime::class.java) { Converter<ZonedDateTime, String> { value -> DateTimeFormatter.ISO_INSTANT.format(value) } } else { super.stringConverter(type, annotations, retrofit) } } }
构建 Retrofit 时将这个 Converter 放在 GsonConverterFactory 前面:
Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(ZonedDateTimeConverterFactory()) .addConverterFactory(GsonConverterFactory.create(gson)) .build()
方案2:让 Gson 处理所有查询参数的序列化
自定义 Converter.Factory,利用已配置好的 Gson 来序列化所有查询参数,这样你的自定义序列化器就能被触发:
class GsonQueryConverterFactory(private val gson: Gson) : Converter.Factory() { override fun stringConverter( type: Type, annotations: Array<Annotation>, retrofit: Retrofit ): Converter<*, String>? { val adapter = gson.getAdapter(TypeToken.get(type)) return Converter<Any, String> { value -> // 去掉Gson序列化后自动添加的引号 adapter.toJson(value).replace("\"", "") } } }
构建 Retrofit 时添加这个 Factory,并确保 Gson 已注册你的序列化器:
val gson = GsonBuilder() .registerTypeAdapter(ZonedDateTime::class.java, ZonedDateTimeSerializer()) .registerTypeAdapter(LocalDate::class.java, LocalDateSerializer()) .create() Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonQueryConverterFactory(gson)) .addConverterFactory(GsonConverterFactory.create(gson)) .build()
优化序列化器代码(可选)
将序列化器的参数名改为更贴合的名称,提升可读性:
class ZonedDateTimeSerializer : JsonSerializer<ZonedDateTime?> { override fun serialize( zonedDateTime: ZonedDateTime?, srcType: Type?, context: JsonSerializationContext? ): JsonElement { val formatter = DateTimeFormatter.ISO_INSTANT return JsonPrimitive(formatter.format(zonedDateTime)) } }
内容的提问来源于stack exchange,提问作者tasjapr
相关产品推荐
相关产品推荐

