Android中处理Gson解析数组/对象混合字段异常的方案咨询
解决Gson解析单个对象/数组兼容的问题
这种服务器返回格式来回跳的坑我遇过好多次了——明明定义的是List<User>,结果有数据的时候给单个User对象,空的时候给数组,直接把Gson给整懵了。不过别担心,Android里有几种靠谱的解决办法,我给你详细说说:
方案一:自定义TypeAdapter(最推荐,一劳永逸)
写一个通用的TypeAdapter,不管服务器返回的是单个对象还是数组,都统一转成List。这种方式最规范,还能复用在其他类似字段上。
public class SingleOrArrayTypeAdapter<T> extends TypeAdapter<List<T>> { private final TypeAdapter<T> itemAdapter; public SingleOrArrayTypeAdapter(TypeAdapter<T> itemAdapter) { this.itemAdapter = itemAdapter; } @Override public void write(JsonWriter out, List<T> value) throws IOException { // 序列化时可选:如果List只有一个元素,写成单个对象(也可以统一写成数组,看需求) if (value != null && value.size() == 1) { itemAdapter.write(out, value.get(0)); } else { out.beginArray(); if (value != null) { for (T item : value) { itemAdapter.write(out, item); } } out.endArray(); } } @Override public List<T> read(JsonReader in) throws IOException { List<T> resultList = new ArrayList<>(); JsonToken peekToken = in.peek(); if (peekToken == JsonToken.BEGIN_OBJECT) { // 单个对象的情况:解析后加入List T item = itemAdapter.read(in); resultList.add(item); } else if (peekToken == JsonToken.BEGIN_ARRAY) { // 数组的情况:正常遍历解析 in.beginArray(); while (in.hasNext()) { T item = itemAdapter.read(in); resultList.add(item); } in.endArray(); } return resultList; } }
使用方式
有两种注册Adapter的方式,选一种就行:
- 方式1:全局注册到Gson实例
Gson gson = new GsonBuilder() .registerTypeAdapter( new TypeToken<List<User>>() {}.getType(), new SingleOrArrayTypeAdapter<>(new Gson().getAdapter(User.class)) ) .create(); // 用这个gson实例解析响应 Courier courier = gson.fromJson(responseJson, Courier.class);
- 方式2:字段上直接加注解
修改你的Courier类,给data字段加上@JsonAdapter注解,这样用默认Gson实例也能解析:
public class Courier { @SerializedName("data") @JsonAdapter(SingleOrArrayTypeAdapter.class) private List<User> data = null; // 构造方法、getter、setter保持不变 public Courier() { } public Courier(List<User> data) { this.data = data; } public List<User> getData() { return data; } public void setData(List<User> data) { this.data = data; } }
方案二:JsonElement手动中转(灵活应急)
如果不想写自定义Adapter,也可以先把整个响应解析成JsonElement,手动处理courier.data的格式:
// 先把响应字符串转成JsonElement JsonElement rootElement = new Gson().fromJson(responseJson, JsonElement.class); JsonObject rootObj = rootElement.getAsJsonObject(); // 找到courier对象 JsonObject courierObj = rootObj.getAsJsonObject("data") .getAsJsonObject("courier"); JsonElement dataElement = courierObj.get("data"); JsonArray processedDataArray; if (dataElement.isJsonObject()) { // 单个对象转成数组 processedDataArray = new JsonArray(); processedDataArray.add(dataElement); } else { // 本来就是数组,直接复用 processedDataArray = dataElement.getAsJsonArray(); } // 把处理后的数组放回courier对象 courierObj.add("data", processedDataArray); // 最后解析成Courier实例 Courier courier = new Gson().fromJson(rootObj, Courier.class);
这种方式代码量稍大,但不需要额外写Adapter,适合偶尔处理这类场景。
方案三:修改模型类为Object类型(不推荐,应急用)
把Courier里的data字段改成Object类型,然后在getter里手动转成List。这种方式虽然简单,但破坏了类型安全性,后续维护容易出问题,除非万不得已不建议用:
public class Courier { @SerializedName("data") private Object data; public List<User> getData() { List<User> userList = new ArrayList<>(); Gson gson = new Gson(); if (data instanceof JsonObject) { User user = gson.fromJson((JsonObject) data, User.class); userList.add(user); } else if (data instanceof JsonArray) { userList = gson.fromJson((JsonArray) data, new TypeToken<List<User>>() {}.getType()); } return userList; } // setter按需调整 public void setData(Object data) { this.data = data; } }
总结
最推荐的是自定义TypeAdapter的方案,不仅解决当前问题,后续其他字段遇到类似格式问题也能复用这个Adapter,符合Gson的扩展规范,代码也更优雅。
内容的提问来源于stack exchange,提问作者nicolas asinovich
相关产品推荐
相关产品推荐

