如何为Retrofit/Gson动态创建类型适配器?求最优实现方案
嘿,针对你遇到的Retrofit解析定制化需求,我来分享几个实用的解决方案,都是实际项目里验证过的:
方案一:自定义Gson TypeAdapter(适配Gson解析场景)
如果你的Retrofit用的是Gson做JSON转义,自定义TypeAdapter是最直接解决继承、字段依赖问题的方式:
- 处理继承与共享字段:先定义一个包含所有共享字段的基类,让业务子类继承它。在TypeAdapter里,先解析用来区分子类的标识字段(比如
type),实例化对应的子类后,再统一解析共享字段+子类独有字段。 - 处理字段依赖:解析时先读取依赖字段的值,再根据这个值决定是否解析后续字段,甚至可以加入逻辑校验(比如某个字段存在时,另一个字段必须非空)。
举个简单的Java代码示例:
public class BaseDataAdapter extends TypeAdapter<BaseData> { @Override public void write(JsonWriter out, BaseData value) throws IOException { // 序列化逻辑(如果需要自定义输出的话) } @Override public BaseData read(JsonReader in) throws IOException { // 先把整个JSON读成JsonElement,方便后续灵活解析 JsonElement jsonElement = new JsonParser().parse(in); JsonObject jsonObject = jsonElement.getAsJsonObject(); // 读取区分类型的字段 String dataType = jsonObject.get("type").getAsString(); BaseData targetData = switch (dataType) { case "user" -> new UserData(); case "product" -> new ProductData(); default -> throw new IllegalArgumentException("Unknown data type: " + dataType); }; // 先解析基类共享字段 targetData.setId(jsonObject.get("id").getAsString()); targetData.setCreateTime(jsonObject.get("create_time").getAsString()); // 根据子类类型解析独有字段 if (targetData instanceof UserData) { ((UserData) targetData).setUserName(jsonObject.get("user_name").getAsString()); } else if (targetData instanceof ProductData) { // 处理字段依赖:如果is_on_sale为true,才解析price字段 boolean isOnSale = jsonObject.get("is_on_sale").getAsBoolean(); ((ProductData) targetData).setOnSale(isOnSale); if (isOnSale) { ((ProductData) targetData).setPrice(jsonObject.get("price").getAsDouble()); } } return targetData; } }
注册这个适配器到Gson,再传给Retrofit:
Gson customGson = new GsonBuilder() .registerTypeAdapter(BaseData.class, new BaseDataAdapter()) .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("your_base_url") .addConverterFactory(GsonConverterFactory.create(customGson)) .build();
方案二:用Moshi的多态适配器(Kotlin项目更友好)
如果是Kotlin项目,Moshi对数据类、多态解析的支持比Gson更简洁,推荐用它的PolymorphicJsonAdapterFactory:
- 先定义密封类(或普通基类)和子类,用固定字段区分类型:
sealed class BaseData { abstract val type: String abstract val id: String abstract val createTime: String } @JsonClass(generateAdapter = true) data class UserData( override val type: String, override val id: String, override val createTime: String, val userName: String ) : BaseData() @JsonClass(generateAdapter = true) data class ProductData( override val type: String, override val id: String, override val createTime: String, val isOnSale: Boolean, val price: Double? // 依赖isOnSale的字段,设为可空 ) : BaseData()
- 创建多态适配器并注册到Moshi:
val moshi = Moshi.Builder() .add(PolymorphicJsonAdapterFactory.of(BaseData::class.java, "type") .withSubtype(UserData::class.java, "user") .withSubtype(ProductData::class.java, "product")) .build() val retrofit = Retrofit.Builder() .baseUrl("your_base_url") .addConverterFactory(MoshiConverterFactory.create(moshi)) .build()
如果要处理字段依赖,可以在数据类里加自定义getter,或者用JsonAdapter.Factory做更细粒度的定制,Moshi的API设计会更顺手。
方案三:自定义CallAdapter(全流程控制)
如果你的需求不仅是解析,还涉及请求前/响应后的逻辑处理(比如根据字段值决定是否发起请求、响应后做数据修正),可以自定义CallAdapter:
- 继承
CallAdapter.Factory,包装原有的Call实例,在enqueue或execute方法里插入自定义逻辑:
public class CustomCallAdapterFactory extends CallAdapter.Factory { @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { CallAdapter<?, ?> delegate = retrofit.nextCallAdapter(this, returnType, annotations); return new CallAdapter<Object, Call<Object>>() { @Override public Type responseType() { return delegate.responseType(); } @Override public Call<Object> adapt(Call<Object> call) { return new WrappedCall<>(call); } }; } private static class WrappedCall<T> implements Call<T> { private final Call<T> delegate; WrappedCall(Call<T> delegate) { this.delegate = delegate; } @Override public void enqueue(Callback<T> callback) { delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, Response<T> response) { // 响应后处理:比如根据字段值修正数据 if (response.body() instanceof ProductData productData) { if (!productData.isOnSale()) { productData.setPrice(null); } } callback.onResponse(call, response); } @Override public void onFailure(Call<T> call, Throwable t) { callback.onFailure(call, t); } }); } // 其他方法直接委托给原Call实例 @Override public Response<T> execute() throws IOException { return delegate.execute(); } @Override public boolean isExecuted() { return delegate.isExecuted(); } @Override public boolean isCanceled() { return delegate.isCanceled(); } @Override public void cancel() { delegate.cancel(); } @Override public Call<T> clone() { return new WrappedCall<>(delegate.clone()); } @Override public Request request() { return delegate.request(); } } }
注册到Retrofit即可:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("your_base_url") .addConverterFactory(GsonConverterFactory.create(customGson)) .addCallAdapterFactory(new CustomCallAdapterFactory()) .build();
总结
- 只是解析层面的继承/字段依赖:优先选Gson自定义TypeAdapter或Moshi多态适配器,后者在Kotlin项目体验更好;
- 需要请求全流程控制:用自定义CallAdapter补充逻辑。
内容的提问来源于stack exchange,提问作者Damn Vegetables
相关产品推荐
相关产品推荐

