Retrofit请求报错:Expected BEGIN_OBJECT but was STRING求助
解决Retrofit+Gson解析时的
JsonSyntaxException问题 看起来你遇到的错误根源有两个:请求体格式不符合服务器预期,以及响应模型和服务器返回的JSON结构不匹配,咱们一步步来修复:
1. 错误原因分析
- 先看报错信息:
Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $,这说明Gson尝试解析JSON对象,但收到的却是纯字符串(大概率是服务器返回的错误提示,因为请求格式不对)。 - 你的接口
getCashCollection直接把String类型的guid作为@Body传递,这会让Retrofit发送纯文本字符串(比如"xxxx-guid-xxxx"),而非服务器期望的JSON对象格式(比如{"guid": "xxxx-guid-xxxx"}),服务器无法正确处理请求,返回了非预期的字符串响应,导致Gson解析失败。 - 退一步说,就算请求正确,服务器返回的JSON是包含
data数组和currency字段的对象,但你定义的接口返回Call<List<CashCollection>>,Gson没法把外层对象直接解析成List,这也会触发解析错误。
2. 具体修复步骤
步骤一:创建请求体模型类
用一个类包裹guid,让Retrofit发送符合要求的JSON请求体:
data class CashCollectionRequest( val guid: String )
步骤二:修改API接口定义
更新接口,使用新的请求体类,同时把返回类型改成匹配服务器响应的模型:
interface ChartsDataApi { @POST(NetworkUtils.CASH_COLLECTION_URL) // 替换String为请求体类,返回类型改为响应模型 fun getCashCollection(@Body request: CashCollectionRequest) : Call<CashCollectionResponse> @POST(NetworkUtils.TICKETS_DETAILS_URL) fun getTicketsDetails() : Call<List<TicketDetails>> }
步骤三:创建响应模型类
这个类要完全匹配服务器返回的JSON结构:
data class CashCollectionResponse( @SerializedName("data") val data: List<CashCollection>, @SerializedName("currency") val currency: String? ) // 你的CashCollection类可以保留不变 data class CashCollection( @SerializedName("label") val label: String?, @SerializedName("value") val value: String? )
步骤四:更新请求实现代码
调整请求调用和响应解析的逻辑:
private fun geCashCollectionDetails(){ val BASE_URL = "这里填写了基础URL" val gson = GsonBuilder() .setLenient() .create() val api = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build() .create(ChartsDataApi::class.java) GlobalScope.launch(Dispatchers.IO){ // 传入请求体对象 val response = api.getCashCollection(CashCollectionRequest("这里填写了guid")).awaitResponse() if(response.isSuccessful){ // 从响应模型中获取data列表 val cashCollectionList = response.body()?.data ?: emptyList() val day = cashCollectionList.lastIndex Log.d("RAZZ", day.toString()) withContext(Dispatchers.Main){ // 这里可以添加UI更新逻辑 } } else { // 处理请求失败的情况,比如打印错误信息 Log.e("RAZZ", "Request failed: ${response.errorBody()?.string()}") } } }
3. 额外注意点
- 如果服务器要求请求体的字段名不是
guid,记得给CashCollectionRequest的字段加上@SerializedName注解,比如@SerializedName("user_guid") val guid: String。 - 建议不要直接用
GlobalScope,可以自定义CoroutineScope,避免内存泄漏问题。 - 测试时可以用抓包工具(比如Charles)确认请求体格式是否正确,以及服务器返回的响应是否符合预期。
内容的提问来源于stack exchange,提问作者Razvan Andrei
相关产品推荐
相关产品推荐

