在Android中使用Retrofit和Kotlin解析无键JSON数组的问题及解决
在Android中使用Retrofit和Kotlin解析无键JSON数组的问题及解决
我最近在开发Android应用时,被API调用和响应解析的问题难住了——原本能正常序列化/Parcelize的API响应格式突然变了,我一时找不到合适的解析方法,折腾了好久才搞定,现在把过程分享出来。
问题背景:API响应格式变更
之前的API返回的是带键的JSON对象数组,每个对象都有明确的字段名,比如:
[ { "msg": "No data available for this time period: 2022-11-14 11:00:00 and 2022-11-14 11:15:00" }, { "datetime": "2022-11-14 11:15:00", "avg_temp": 27.59265899658203, "min_temp": 27.59265899658203, "max_temp": 27.59265899658203, "open_temp": 27.59265899658203, "close_temp": 27.59265899658203 } ]
但后来API返回格式改成了无键的二维数组,每个子数组的元素对应原来的字段值,顺序是固定的:
[ [ 1668413700, -99, 29, 27.59, 27.59, 27.59, 27.59, 27.59 ], [ 1668414600, -99, 29, 27.59, 27.59, 27.59, 27.59, 27.59 ] ]
最初的实现尝试
我之前已经定义了带@Parcelize和@SerializedName注解的数据类,用来解析旧的响应格式:
@Parcelize data class TRA( @SerializedName("datetime") val datetime: Long?, @SerializedName("pred") val pred: Float?, @SerializedName("sensor") val sensor: Int?, @SerializedName("average") val average: Float?, @SerializedName("min") val min: Float?, @SerializedName("max") val max: Float?, @SerializedName("open") val open: Float?, @SerializedName("close") val close: Float?, @SerializedName("msg") val msg: String? ): Parcelable
然后在Repository里尝试手动解析新的响应,用JSONArray循环处理每个子数组,映射到TRA对象:
suspend fun uploadId(request: TextRecognitionRequest): List<TRA>? { return try { val response = api.uploadSensorId(request = request) val jsonArray = JSONArray(response) val resultList = mutableListOf<TRA>() for (i in 0 until jsonArray.length()) { val json = jsonArray.getJSONArray(i) val item = TRA( datetime = json.optInt(0).toLong(), pred = json.optDouble(1).toFloat(), sensor = json.optInt(2), average = json.optDouble(3).toFloat(), min = json.optDouble(4).toFloat(), max = json.optDouble(5).toFloat(), open = json.optDouble(6).toFloat(), close = json.optDouble(7).toFloat(), ) resultList.add(item) } resultList } catch (e: Exception) { e.printStackTrace() null } }
ViewModel和MainActivity里的观测逻辑是这样的:
@HiltViewModel class MainActivityViewModel @Inject constructor( private val repository: CameraRepository ) : ViewModel() { private val _sensorResponse = MutableLiveData<List<TRA>>() val sensorResponse: LiveData<List<TRA>> = _sensorResponse fun uploadSensorId(request: TextRecognitionRequest) { viewModelScope.launch { _sensorResponse.postValue(repository.uploadSensorId(request)) } } }
private fun lineChartForDataObservation() { viewModel.sensorResponse.observe(this) { response -> response?.let { safeResponse -> val values = arrayListOf<Float>() val valuesPred = arrayListOf<Float>() val valuesClose = arrayListOf<Float>() val valuesOpen = arrayListOf<Float>() val valuesMax = arrayListOf<Float>() val valuesMin = arrayListOf<Float>() dates = arrayListOf() val sensors = arrayListOf<Int>() for (item in safeResponse) { item.datetime?.let { dates.add(it.toString()) } item.pred?.let { valuesPred.add(it) } item.average?.let { values.add(it) } item.open?.let { valuesOpen.add(it) } item.close?.let { valuesClose.add(it) } item.min?.let { valuesMin.add(it) } item.max?.let { valuesMax.add(it) } } chartModels.add( AASeriesElement() .name(sensors.component1().toString()) .data(values.toTypedArray()) .allowPointSelect(true) .dashStyle(AAChartLineDashStyleType.Solid) ) drawChart() } ?: run { Toast.makeText(this, "Something went wrong!", Toast.LENGTH_LONG).show() startActivity(Intent(this, OpenCameraActivity::class.java)) } } }
Retrofit的配置是用Gson转换器:
@Provides @Singleton fun provideCameraApi(okHttpClient: OkHttpClient): CameraApi { return Retrofit.Builder() .baseUrl(Constants.BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() .create(CameraApi::class.java) }
解决方法
折腾了半天后发现,问题出在Retrofit接口的返回类型定义上!原来的接口可能定义成了返回List<TRA>或者其他对象类型,但新的响应是二维数组,所以需要把接口的返回类型改成Array<Array<Double>>,这样Retrofit能正确解析原始的数组响应,之后再手动映射到TRA对象。
修改后的API接口示例:
@POST("your-api-endpoint") suspend fun uploadSensorId( @Header("Accept") header1: String = "your-header-value", @Header("User-Agent") header2: String = "your-header-value", @Header("Connection") header5: String= "your-header-value", @Body request: TextRecognitionRequest, ) : Array<Array<Double>>
这样修改后,Repository里的解析逻辑就能正常拿到二维数组,再循环把每个子数组的元素对应到TRA的字段上,整个流程就跑通了!
备注:内容来源于stack exchange,提问作者Kaljal
相关产品推荐
相关产品推荐

