You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:18:27