Retrofit2中Kotlin序列化JSON报错的排查与疑问
解决Retrofit+Kotlin序列化解析Google Books API响应的问题
问题背景
开发Bookshelf应用时,调用Google Books API的https://www.googleapis.com/books/v1/volumes?q=jazz+history接口,使用Retrofit2搭配kotlinx-serialization做序列化解析时,出现JsonDecodingException: Expected start of the array '[', but had 'EOF' instead错误,以下是针对疑问的解答:
1. 如何调试此类解析问题?
- 打印原始响应内容:临时修改接口返回类型为
Response<ResponseBody>,请求后通过response.body()?.string()获取完整的JSON响应字符串(注意该方法只能调用一次,调用后响应体流会关闭),直接查看返回的JSON结构。 - 校验JSON与数据类匹配度:把拿到的JSON字符串用格式化工具整理后,对比自己定义的
QueryResponse数据类字段,确认字段名、层级、类型是否完全对应。 - 添加日志拦截器:给Retrofit添加OkHttp的日志拦截器,在Logcat中打印完整的请求和响应详情,包括HTTP状态码、响应头、响应体内容,方便定位问题。
- 手动解析测试:拿到原始JSON后,用
Json.decodeFromString<QueryResponse>(jsonString)手动测试解析,逐步排查数据类的匹配问题。
2. 如何确保解析的是响应体而非整个响应内容?
Retrofit默认只会解析HTTP响应的响应体(Response Body),不会包含响应头、状态码等其他响应信息,你遇到的问题和是否解析响应体无关,而是返回类型不匹配导致的。要确保正确解析,注意两点:
- 接口返回类型要匹配JSON结构:Google Books API的返回是单个
QueryResponse对象,所以接口方法的返回类型应该是QueryResponse或Response<QueryResponse>,而非List<QueryResponse>。 - 正确配置转换器:你当前使用
Json.asConverterFactory("application/json".toMediaType())的配置是正确的,只要Retrofit对象构建无误,就会自动将响应体的JSON字符串解析为对应的数据类。
3. 为何用Gson+Response的实现能正常运行?
核心原因是返回类型匹配:
- 第一种实现中,接口方法返回
List<QueryResponse>,但API返回的是单个对象而非数组,kotlinx-serialization会尝试将对象解析为数组,直接触发格式错误导致崩溃。 - 第二种实现中,接口返回
Response<QueryResponse>,完全匹配API的JSON结构(外层是单个对象),而且代码中先判断res.isSuccessful再安全获取body,即使响应出现非200状态码,也不会直接崩溃。另外Gson本身对格式不匹配的容错性略高于kotlinx-serialization,但最关键的还是返回类型的修正。
修复后的正确代码示例
修正后的接口方法
@GET("volumes") suspend fun getBooks(@Query("q") query: String): QueryResponse
或者用Response类型做安全处理:
@GET("volumes") suspend fun getBooks(@Query("q") query: String): Response<QueryResponse>
调用示例
override suspend fun getBooks(query: String): List<Book>? { return try { val response = bookshelfApiService.getBooks(query) response.body()?.items ?: emptyList() } catch (e: Exception) { // 处理异常 emptyList() } }
内容的提问来源于stack exchange,提问作者Catosh
相关产品推荐
相关产品推荐

