Android Kotlin中基于Retrofit+Dagger2实现通用TypeAdapterFactory与TypeAdapter
通用Gson TypeAdapter解决方案(兼容对象/数组、Double转Int)
针对你提出的两个核心场景,以下是基于TypeAdapter和TypeAdapterFactory的通用实现方案,完全符合你不使用JSONDeserializer的要求,适配MVVM+Retrofit+Dagger2架构:
场景1:同一键(含嵌套)兼容JSONObject/JSONArray
通过自定义注解标记需要兼容的字段,配合Factory实现自动识别处理:
1. 定义标记注解
@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD) annotation class SingleOrList
2. 实现通用TypeAdapterFactory
import com.google.gson.Gson import com.google.gson.TypeAdapter import com.google.gson.TypeAdapterFactory import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter import java.lang.reflect.ParameterizedType import java.util.Collections class SingleOrListTypeAdapterFactory : TypeAdapterFactory { override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { val rawType = type.rawType as Class<T> // 仅处理List类型且带有SingleOrList注解的字段 if (!List::class.java.isAssignableFrom(rawType)) return null // 获取List的泛型元素类型 val elementType = (type.type as? ParameterizedType)?.actualTypeArguments?.get(0) ?: Any::class.java val elementAdapter = gson.getAdapter(TypeToken.get(elementType)) return object : TypeAdapter<T>() { override fun write(out: JsonWriter, value: T) { // 写入时直接使用元素适配器,支持单个元素或列表 val list = value as List<*> if (list.size == 1) elementAdapter.write(out, list[0]) else gson.toJson(list, type.type, out) } override fun read(`in`: JsonReader): T { return when (`in`.peek()) { JsonToken.BEGIN_ARRAY -> gson.fromJson(`in`, type.type) JsonToken.BEGIN_OBJECT -> { // 将单个对象包装为List返回 val element = elementAdapter.read(`in`) Collections.singletonList(element) as T } else -> throw JsonParseException("Expected BEGIN_ARRAY or BEGIN_OBJECT but was ${`in`.peek()}") } } } } }
3. 数据类中使用注解
data class CartResponse( @SingleOrList val items: List<OfferItem>, // 其他字段... ) data class OfferItem( // 嵌套字段同样支持 @SingleOrList val details: List<Detail>, // 其他字段... )
场景2:Double类型响应转为Int存储
实现全局或字段级的Double转Int适配器:
全局Int类型适配(所有Int字段自动处理)
import com.google.gson.TypeAdapter import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter class DoubleToIntTypeAdapter : TypeAdapter<Int>() { override fun write(out: JsonWriter, value: Int) { // 写入时转回Double适配接口要求 out.value(value.toDouble()) } override fun read(`in`: JsonReader): Int { return when (`in`.peek()) { JsonToken.NUMBER -> `in`.nextDouble().toInt() // 直接取整,可按需改为四舍五入 JsonToken.STRING -> `in`.nextString().toDouble().toInt() // 兼容字符串格式数字 else -> throw JsonParseException("Expected NUMBER or STRING but was ${`in`.peek()}") } } }
如果需要仅针对特定字段处理,可参考场景1的注解+Factory模式实现精准匹配。
整合到Dagger2配置
替换你现有的Gson提供逻辑,注册上述适配器和工厂:
@Provides @Singleton fun provideGson(): Gson { return GsonBuilder() .registerTypeAdapterFactory(SingleOrListTypeAdapterFactory()) .registerTypeAdapter(Int::class.java, DoubleToIntTypeAdapter()) .create() } @Provides @Singleton fun provideCartGsonConverterFactory(gson: Gson): GsonConverterFactory { return GsonConverterFactory.create(gson) }
现有代码问题修正
你当前的CustomTypeAdapter和CustomTypeAdapterFactory存在逻辑错误:
CustomTypeAdapter的write方法错误绑定了OfferItem::class.java,应动态使用传入的classOfTread方法存在非空类型赋值null、逻辑不完整的问题CustomTypeAdapterFactory仅处理Enum类型,不符合你的需求,建议替换为上述实现
内容的提问来源于stack exchange,提问作者Ishika Singh
相关产品推荐
相关产品推荐

