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

如何在Retrofit中处理Null响应?我在多处遇到此问题

嘿,我太懂你在Retrofit里处理JSON空值/Null值时的抓狂了——你给的示例里,一会儿genre是null,一会儿是数组,story是空字符串,release_date又是null,还有censor_rating用 凑数,这种混合的空值表示确实坑人。我来给你几个实用的解决方案,分场景搞定:

1. 先搞定基础的Null字段(比如release_date、story)

首先,在你的数据模型(不管是Kotlin Data Class还是Java Bean)里,把可能为Null的字段声明为可空类型:

  • Kotlin里直接加?,比如val release_date: String?
  • Java里用对象类型(比如String而不是int,因为Primitive类型不能为null)

以Kotlin为例,你的Movie数据类可以先这么定义:

data class Movie(
    val name: String, // 假设这个字段永远非空
    val full_movie: String,
    val story: String?, // 允许空字符串或null
    val genre: Any?, // 先设为Any?,因为类型不稳定,后面单独处理
    val censor_rating: String?,
    val release_date: String?
)

Gson默认支持解析Null到可空字段,Moshi等序列化库也只需要开启可空支持即可,不用额外复杂配置。

2. 处理混合类型的字段(比如genre有时是null,有时是数组)

这是最棘手的情况,同一个JSON字段可能返回不同类型。这里有两种靠谱的解决方式:

方法一:自定义Type Adapter(精准处理单个字段)

给genre字段写一个专属的类型转换器,让它自动识别JSON类型并转换:

class GenreTypeAdapter : TypeAdapter<List<String>?>() {
    override fun write(out: JsonWriter, value: List<String>?) {
        // 序列化逻辑(如果需要把对象转成JSON的话)
        value?.let {
            out.beginArray()
            it.forEach { genre -> out.value(genre) }
            out.endArray()
        } ?: out.nullValue()
    }

    override fun read(`in`: JsonReader): List<String>? {
        return when (`in`.peek()) {
            JsonToken.NULL -> {
                `in`.nextNull()
                null
            }
            JsonToken.BEGIN_ARRAY -> {
                val genres = mutableListOf<String>()
                `in`.beginArray()
                while (`in`.hasNext()) {
                    genres.add(`in`.nextString())
                }
                `in`.endArray()
                genres
            }
            else -> {
                // 遇到其他意外类型(比如字符串),直接跳过并返回空列表
                `in`.skipValue()
                emptyList()
            }
        }
    }
}

然后在创建Retrofit的时候,把这个Adapter注册到Gson里:

val gson = GsonBuilder()
    .registerTypeAdapter(
        object : TypeToken<List<String>?>() {}.type, 
        GenreTypeAdapter()
    )
    .create()

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()

现在你的数据类里genre就可以改成val genre: List<String>?,不用再用Any?啦。

方法二:全局JsonDeserializer(处理整个对象的解析)

如果不想单独写TypeAdapter,也可以写一个全局的解析器,手动处理每个字段的特殊情况:

class MovieDeserializer : JsonDeserializer<Movie> {
    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Movie {
        val jsonObj = json.asJsonObject
        
        val name = jsonObj.get("name").asString
        val fullMovie = jsonObj.get("full_movie").asString
        // 把空字符串也转成null
        val story = jsonObj.get("story").takeIf { !it.isJsonNull && it.asString.isNotEmpty() }?.asString
        // 处理genre的类型切换
        val genre = when {
            jsonObj.get("genre").isJsonNull -> null
            jsonObj.get("genre").isJsonArray -> context.deserialize<List<String>>(jsonObj.get("genre"), object : TypeToken<List<String>>() {}.type)
            else -> emptyList()
        }
        // 把&amp;nbsp;转换成null
        val censorRating = jsonObj.get("censor_rating").asString.takeIf { it != "&amp;nbsp;" }
        val releaseDate = jsonObj.get("release_date").takeIf { !it.isJsonNull }?.asString

        return Movie(name, fullMovie, story, genre, censorRating, releaseDate)
    }
}

注册方式类似:

val gson = GsonBuilder()
    .registerTypeAdapter(Movie::class.java, MovieDeserializer())
    .create()
3. 处理特殊空值(比如censor_rating的&nbsp;)

像示例里的&amp;nbsp;(本质是HTML空格转义),可以在解析的时候直接替换成null或者空字符串,就像上面MovieDeserializer里的处理逻辑:val censorRating = jsonObj.get("censor_rating").asString.takeIf { it != "&amp;nbsp;" }。

如果这类特殊值很多,也可以写一个通用的字符串转换器,统一处理所有字符串字段的替换。

4. 终极建议

最省心的办法还是和后端同学沟通,统一JSON返回格式:

  • 数组类型的字段(比如genre)空的时候返回空数组[],而不是null
  • 空值统一用null或者空字符串,不要混用
  • 特殊占位符(比如&nbsp;)直接返回null或空字符串

这样前端解析逻辑会简单很多,避免各种奇怪的兼容问题。

内容的提问来源于stack exchange,提问作者Abhishek Mishra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:00:25