如何使用Retrofit解析包含不同大小对象与数组的JSON对象?
嘿,我看你在纠结用Retrofit解析那种混着不同属性对象的JSON数组对吧?我之前也踩过这个坑,给你分享几个亲测好用的解法:
1. 自定义Gson TypeAdapter(灵活适配复杂场景)
如果你的JSON数组里的对象差异比较大,比如有的是文章、有的是视频,结构完全不一样,自定义TypeAdapter是最灵活的方案。
先举个典型的JSON示例:
[
{"type": "article", "title": "Retrofit实战技巧", "content": "详细解析Retrofit的各种用法..."},
{"type": "video", "title": "Gson多态解析教程", "duration": 180}
]
第一步:定义基类和子类
先抽取出所有对象的公共字段作为基类,再分别定义不同类型的子类:
// 基类,存放公共字段 public abstract class BaseItem { public String type; } // 文章类型的子类 public class ArticleItem extends BaseItem { public String title; public String content; } // 视频类型的子类 public class VideoItem extends BaseItem { public String title; public int duration; }
第二步:编写自定义TypeAdapter
这个Adapter的作用就是根据type字段判断对象类型,然后解析成对应的子类:
public class ItemTypeAdapter extends TypeAdapter<BaseItem> { private final Gson gson = new Gson(); @Override public void write(JsonWriter out, BaseItem value) throws IOException { // 序列化的时候直接用对应子类的类型解析 gson.toJson(value, value.getClass(), out); } @Override public BaseItem read(JsonReader in) throws IOException { // 先把JSON读成JsonObject,拿到type字段 JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject(); String itemType = jsonObject.get("type").getAsString(); // 根据type字段解析成对应的子类 switch (itemType) { case "article": return gson.fromJson(jsonObject, ArticleItem.class); case "video": return gson.fromJson(jsonObject, VideoItem.class); default: throw new IllegalArgumentException("遇到未知的对象类型:" + itemType); } } }
第三步:给Retrofit配置自定义Gson
把这个TypeAdapter注册到Gson里,再传给Retrofit的转换器:
// 构建自定义Gson实例 Gson customGson = new GsonBuilder() .registerTypeAdapter(BaseItem.class, new ItemTypeAdapter()) .create(); // 构建Retrofit Retrofit retrofit = new Retrofit.Builder() .baseUrl("你的接口基础URL") .addConverterFactory(GsonConverterFactory.create(customGson)) .build();
之后你的接口就可以直接用Call<List<BaseItem>>来接收数据了,拿到列表后用instanceof判断类型再做后续处理就行。
2. 用RuntimeTypeAdapterFactory(简洁的多态解析)
如果你的对象是基于某个固定字段(比如上面的type)来区分类型的,Gson的扩展库提供了RuntimeTypeAdapterFactory,能让代码更简洁。
第一步:添加依赖
先在你的build.gradle里加Gson扩展库的依赖:
implementation 'com.google.code.gson:gson-extras:2.10.1'
第二步:配置TypeAdapterFactory
这个工厂类会自动根据指定的字段来匹配对应的子类:
RuntimeTypeAdapterFactory<BaseItem> typeAdapterFactory = RuntimeTypeAdapterFactory .of(BaseItem.class, "type") // 指定基类和区分字段 .registerSubtype(ArticleItem.class, "article") // 注册子类和对应的type值 .registerSubtype(VideoItem.class, "video"); // 构建自定义Gson Gson customGson = new GsonBuilder() .registerTypeAdapterFactory(typeAdapterFactory) .create();
剩下的Retrofit配置和接口定义和第一种方法完全一样,这个方式代码量更少,适合类型比较规整的场景。
3. 兜底方案:先解析成JsonObject再手动处理
如果你不想折腾自定义Adapter,或者只是临时验证接口,也可以先把整个数组解析成List<JsonObject>,然后逐个手动解析:
Call<List<JsonObject>> call = apiService.getMixedItems(); call.enqueue(new Callback<List<JsonObject>>() { @Override public void onResponse(Call<List<JsonObject>> call, Response<List<JsonObject>> response) { if (response.isSuccessful() && response.body() != null) { List<BaseItem> itemList = new ArrayList<>(); Gson gson = new Gson(); for (JsonObject jsonObj : response.body()) { String type = jsonObj.get("type").getAsString(); if ("article".equals(type)) { itemList.add(gson.fromJson(jsonObj, ArticleItem.class)); } else if ("video".equals(type)) { itemList.add(gson.fromJson(jsonObj, VideoItem.class)); } } // 拿到itemList后做业务处理 } } @Override public void onFailure(Call<List<JsonObject>> call, Throwable t) { // 处理请求失败的情况 Log.e("Retrofit", "请求失败", t); } });
这个方式虽然代码繁琐一点,但胜在直观,适合快速验证或者类型很少的场景。
内容的提问来源于stack exchange,提问作者ProGrammar

