使用Gson处理Retrofit API响应中的动态属性类型问题
解决Retrofit中API响应属性在空数组与Map间切换的解析问题
这个坑我之前踩过!API返回格式不一致确实挺闹心的——同一个字段空的时候返回[],有数据的时候返回Map<String, MyObject>,默认的序列化器肯定搞不定这种情况,得自定义解析逻辑来兼容两种格式。下面分Gson和Moshi两种常用序列化库给你讲具体实现:
方法一:用Gson自定义反序列化器
1. 编写Map与空数组的适配解析器
创建一个JsonDeserializer的实现类,判断JSON元素是数组还是对象,分别处理:
public class MapOrEmptyArrayDeserializer implements JsonDeserializer<Map<String, MyObject>> { @Override public Map<String, MyObject> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // 如果是空数组,直接返回空Map if (json.isJsonArray()) { return new HashMap<>(); } // 如果是对象,按正常Map逻辑解析 else if (json.isJsonObject()) { return context.deserialize(json, typeOfT); } // 其他异常情况,可根据需求抛出异常或返回空Map throw new JsonParseException("Unexpected JSON type: expected array or object"); } }
2. 注册解析器到Gson
在构建Gson实例时,把上面的解析器注册到对应的Map类型:
Gson customGson = new GsonBuilder() .registerTypeAdapter( new TypeToken<Map<String, MyObject>>() {}.getType(), new MapOrEmptyArrayDeserializer() ) .create();
3. 配置Retrofit使用自定义Gson
把这个自定义的Gson实例传给Retrofit的Gson转换器:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("你的API基础地址") .addConverterFactory(GsonConverterFactory.create(customGson)) .build();
方法二:用Moshi自定义适配器(Kotlin场景更常用)
如果你的项目用Moshi做序列化,可以写一个类似的适配器:
1. 编写适配类
class MapOrEmptyArrayAdapter { @FromJson fun fromJson(jsonReader: JsonReader): Map<String, MyObject> { return when (jsonReader.peek()) { // 遇到数组直接跳过,返回空Map JsonReader.Token.BEGIN_ARRAY -> { jsonReader.beginArray() jsonReader.endArray() emptyMap() } // 遇到对象按正常Map解析 else -> { val adapter = Moshi.Builder() .build() .adapter<Map<String, MyObject>>( Types.newParameterizedType( Map::class.java, String::class.java, MyObject::class.java ) ) adapter.fromJson(jsonReader) ?: emptyMap() } } } }
2. 注册适配器到Moshi并配置Retrofit
val customMoshi = Moshi.Builder() .add(MapOrEmptyArrayAdapter()) .build() val retrofit = Retrofit.Builder() .baseUrl("你的API基础地址") .addConverterFactory(MoshiConverterFactory.create(customMoshi)) .build()
注意事项
- 确保
MyObject类本身是可正常序列化/反序列化的,字段和API返回的属性名对应(可以用@SerializedName注解做映射); - 如果你的业务中希望空数组时返回空List而非Map,只需要把解析器的返回类型改成
List<MyObject>,逻辑调整为解析数组时返回空列表即可; - 测试时记得覆盖两种场景:返回空数组和返回正常Map的情况,确保解析都能正常工作。
内容的提问来源于stack exchange,提问作者Brian Hoang
相关产品推荐
相关产品推荐

