如何解决Retrofit+Jetpack Compose中的JSON解析类型不匹配错误?
解决Retrofit解析BigDataCloud逆地理编码API的错误
错误本质
Expected BEGIN_ARRAY but was BEGIN_OBJECT 错误的核心是:你在Retrofit接口里声明的返回类型是数组/List,但API实际返回的是单个JSON对象,两者不匹配导致解析失败。
分步解决
1. 匹配API返回的根结构
先明确BigDataCloud逆地理编码API的返回格式,典型返回示例如下(根节点是单个对象):
{ "latitude": 51.5073509, "longitude": -0.1277583, "administrative": [ { "name": "England", "level": 1, "code": "ENG" }, { "name": "Greater London", "level": 2, "code": "LND" } ] }
这里根节点是{}包裹的对象,不是[]包裹的数组,所以接口返回类型必须对应这个根对象。
2. 修正数据类定义
需要定义对应根对象和administrative子项的数据类:
// 对应administrative数组中的单个区域数据 data class AdministrativeArea( val name: String, val level: Int, val code: String? // 可选字段,根据API实际返回调整 ) // 对应API返回的根对象 data class ReverseGeoCodeResponse( val administrative: List<AdministrativeArea>, // 可按需添加其他需要的字段,比如latitude、longitude等 )
如果API返回的administrative是单个对象而非数组,把List<AdministrativeArea>改成AdministrativeArea即可。
3. 修正ApiService接口
将接口的返回类型改为根对象类型,以Coroutines为例:
interface ApiService { @GET("reverse-geocode-client") suspend fun getReverseGeoCode( @Query("latitude") lat: Double, @Query("longitude") lon: Double, @Query("key") apiKey: String // 若API需要密钥则添加 ): Response<ReverseGeoCodeResponse> }
如果用传统Call方式,改为Call<ReverseGeoCodeResponse>即可。
4. ViewModel中正确提取数据
在ViewModel里调用接口后,提取administrative字段并更新状态:
class GeoCodeViewModel(private val apiService: ApiService) : ViewModel() { private val _administrativeAreas = MutableStateFlow<List<AdministrativeArea>>(emptyList()) val administrativeAreas = _administrativeAreas.asStateFlow() fun fetchReverseGeoCode(lat: Double, lon: Double, apiKey: String) { viewModelScope.launch { try { val response = apiService.getReverseGeoCode(lat, lon, apiKey) if (response.isSuccessful) { response.body()?.let { _administrativeAreas.value = it.administrative } } // 可添加请求失败的处理逻辑 } catch (e: Exception) { // 处理网络或解析异常 } } } }
5. Jetpack Compose中展示数据
在Compose组件中收集状态并渲染:
@Composable fun GeoCodeScreen(viewModel: GeoCodeViewModel) { val administrativeAreas by viewModel.administrativeAreas.collectAsState() LazyColumn { items(administrativeAreas) { area -> Text(text = "${area.name} (等级:${area.level})") } } }
额外排查点
- 检查API请求参数是否完整,漏传必填参数可能导致返回错误格式的JSON(比如错误信息对象)
- 确保数据类字段名与API返回的键完全一致,若字段名不一致,使用
@SerializedName注解匹配,例如:@SerializedName("admin_level") val level: Int
内容的提问来源于stack exchange,提问作者kucingcat
相关产品推荐
相关产品推荐

