使用 moshi 和 retrofit2 解析 JSON 个字符串
Parsing JSON Strings with moshi and retrofit2
我正在使用的 API 响应一个嵌套在列表中的 JSON 对象,如下所示:
[
{
"name": "Seattle",
"lat": 47.6038321,
"lon": -122.3300624,
"country": "US",
"state": "Washington"
}
]
我想用 Moshi 将 JSON 解析为以下内容 class:
package com.example.weatherapp.entity
// the main JSON response
data class LocationWeather(val name: String, val lat: Float, val lon: Float)
我的API服务文件如下
package com.example.weatherapp.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.http.GET
import com.example.weatherapp.entity.LocationWeather
import retrofit2.http.Query
private const val BASE_URL = "http://api.openweathermap.org/geo/1.0/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<LocationWeather>
}
object WeatherApi {
val retrofitService : WeatherAPIService by lazy {
retrofit.create(WeatherAPIService::class.java)
}
}
我的ViewModel,实际连接到API,如下:
package com.example.weatherapp.overview
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.weatherapp.entity.LocationWeather
import com.example.weatherapp.network.WeatherApi
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class OverviewViewModel : ViewModel() {
// the mutable backing property
private var _response = MutableLiveData<String>()
// the immutable exposed property
val response: LiveData<String>
get() = _response
fun getWeather(location: String) {
WeatherApi.retrofitService.getWeatherFromAPI(location, 1, "API_KEY_REDACTED").enqueue(
object: Callback<LocationWeather> {
override fun onResponse(call: Call<LocationWeather>, response: Response<LocationWeather>) {
_response.value = response.body()?.name
}
override fun onFailure(call: Call<LocationWeather>, t: Throwable) {
_response.value = "Failure: " + t.message
}
}
)
}
}
与此 ViewModel 关联的片段仅观察响应 LiveData 并将值呈现给 TextView。呈现的值是“失败:预期 BEGIN_OBJECT 但在路径 $ 处是 BEGIN_ARRAY”。我认为问题在于,由于 JSON 嵌套在列表中,因此值未正确存储在 LocationWeather 数据 class 中。否则代码编译并且模拟器运行。尝试将同一代码与未嵌套在列表中的不同 API 一起使用完全按预期工作(前提是我更新了 LocationWeather 中的参数名称)。然而,当被迫解析上面的 JSON 时,我不知所措。我阅读了 moshi 文档和其他几篇文章,但我不完全确定如何使用适配器或 Types.newParameterizedType() 方法实现他们的解决方案。
如何使用 moshi 和 retrofit2 解析此 JSON?
示例响应数据为数组。
[
{
"name": "Seattle",
"lat": 47.6038321,
"lon": -122.3300624,
"country": "US",
"state": "Washington"
}
]
[] <- 这是数组。
更改响应类型。
这是例子。
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<List<LocationWeather>>
}
您的 API 正在 return 获取 LocationWeather 对象列表,但您的代码试图获取单个 LocationWeather 对象。因此,它抛出了上述异常。
更新您的代码以解决问题:
fun getWeather(location: String) {
WeatherApi.retrofitService.getWeatherFromAPI(location, 1, "API_KEY_REDACTED").enqueue(
object: Callback<List<LocationWeather>> {
override fun onResponse(call: Call<List<LocationWeather>>, response: Response<List<LocationWeather>>) {
// here I'm trying to access the first element of the list using 0th index.
_response.value = response.body()[0]?.name
}
override fun onFailure(call: Call<LocationWeather>, t: Throwable) {
_response.value = "Failure: " + t.message
}
}
)
}
您还必须更新接口中方法的 return 类型:
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<List<LocationWeather>>
}
我正在使用的 API 响应一个嵌套在列表中的 JSON 对象,如下所示:
[
{
"name": "Seattle",
"lat": 47.6038321,
"lon": -122.3300624,
"country": "US",
"state": "Washington"
}
]
我想用 Moshi 将 JSON 解析为以下内容 class:
package com.example.weatherapp.entity
// the main JSON response
data class LocationWeather(val name: String, val lat: Float, val lon: Float)
我的API服务文件如下
package com.example.weatherapp.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.http.GET
import com.example.weatherapp.entity.LocationWeather
import retrofit2.http.Query
private const val BASE_URL = "http://api.openweathermap.org/geo/1.0/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<LocationWeather>
}
object WeatherApi {
val retrofitService : WeatherAPIService by lazy {
retrofit.create(WeatherAPIService::class.java)
}
}
我的ViewModel,实际连接到API,如下:
package com.example.weatherapp.overview
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.weatherapp.entity.LocationWeather
import com.example.weatherapp.network.WeatherApi
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class OverviewViewModel : ViewModel() {
// the mutable backing property
private var _response = MutableLiveData<String>()
// the immutable exposed property
val response: LiveData<String>
get() = _response
fun getWeather(location: String) {
WeatherApi.retrofitService.getWeatherFromAPI(location, 1, "API_KEY_REDACTED").enqueue(
object: Callback<LocationWeather> {
override fun onResponse(call: Call<LocationWeather>, response: Response<LocationWeather>) {
_response.value = response.body()?.name
}
override fun onFailure(call: Call<LocationWeather>, t: Throwable) {
_response.value = "Failure: " + t.message
}
}
)
}
}
与此 ViewModel 关联的片段仅观察响应 LiveData 并将值呈现给 TextView。呈现的值是“失败:预期 BEGIN_OBJECT 但在路径 $ 处是 BEGIN_ARRAY”。我认为问题在于,由于 JSON 嵌套在列表中,因此值未正确存储在 LocationWeather 数据 class 中。否则代码编译并且模拟器运行。尝试将同一代码与未嵌套在列表中的不同 API 一起使用完全按预期工作(前提是我更新了 LocationWeather 中的参数名称)。然而,当被迫解析上面的 JSON 时,我不知所措。我阅读了 moshi 文档和其他几篇文章,但我不完全确定如何使用适配器或 Types.newParameterizedType() 方法实现他们的解决方案。
如何使用 moshi 和 retrofit2 解析此 JSON?
示例响应数据为数组。
[
{
"name": "Seattle",
"lat": 47.6038321,
"lon": -122.3300624,
"country": "US",
"state": "Washington"
}
]
[] <- 这是数组。
更改响应类型。 这是例子。
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<List<LocationWeather>>
}
您的 API 正在 return 获取 LocationWeather 对象列表,但您的代码试图获取单个 LocationWeather 对象。因此,它抛出了上述异常。
更新您的代码以解决问题:
fun getWeather(location: String) {
WeatherApi.retrofitService.getWeatherFromAPI(location, 1, "API_KEY_REDACTED").enqueue(
object: Callback<List<LocationWeather>> {
override fun onResponse(call: Call<List<LocationWeather>>, response: Response<List<LocationWeather>>) {
// here I'm trying to access the first element of the list using 0th index.
_response.value = response.body()[0]?.name
}
override fun onFailure(call: Call<LocationWeather>, t: Throwable) {
_response.value = "Failure: " + t.message
}
}
)
}
您还必须更新接口中方法的 return 类型:
interface WeatherAPIService {
@GET("direct?")
fun getWeatherFromAPI(@Query("q") loc: String,
@Query("limit") lim: Int,
@Query("appid") key: String): Call<List<LocationWeather>>
}