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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:11:04