无法在 Android 中使用 Retrofit 解析 JSON
Unable to parse JSON using Retrofit in Android
我成功命中 API 并获得 json 结果。我可以通过打印 Retrofit 响应正文在日志中看到成功结果。并且还使用 Stetho 作为网络拦截器。
但是,我无法理解为什么 api 响应在存储库的 onResponse()
方法中仍然是 "null"。我相信,我没有传递正确的模型可能是为了 JSON 被正确解析?谁能帮我找出这里的问题是什么?
以下是json:
{
"photos": {
"page": 1,
"pages": 2864,
"perpage": 100,
"total": "286373",
"photo": [
{
"id": "49570734898",
"owner": "165034061@N07",
"secret": "f3cb2c2590",
"server": "65535",
"farm": 66,
"title": "Hello",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
],
"photo": [
{
"id": "12344",
"owner": "23444@N07",
"secret": "f233edd",
"server": "65535",
"farm": 66,
"title": "Hey",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
]
},
"stat": "ok"
}
我的 Pojo Class :
data class Photos(
@SerializedName("page")
val page: Int,
@SerializedName("pages")
val pages: Int,
@SerializedName("perpage")
val perpage: Int,
@SerializedName("photo")
val photos: List<Photo>,
@SerializedName("total")
val total: String
)
data class Photo(
@SerializedName("farm")
val farm: Int,
@SerializedName("id")
val id: String,
@SerializedName("isfamily")
val isFamily: Int,
@SerializedName("isfriend")
val isFriend: Int,
@SerializedName("ispublic")
val isPublic: Int,
@SerializedName("owner")
val owner: String,
@SerializedName("secret")
val secret: String,
@SerializedName("server")
val server: String,
@SerializedName("title")
val title: String
)
改造客户端:
object ApiClient {
private val API_BASE_URL = "https://api.flickr.com/"
private var servicesApiInterface: ServicesApiInterface? = null
fun build(): ServicesApiInterface? {
val builder: Retrofit.Builder = Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.addInterceptor(interceptor()).addNetworkInterceptor(StethoInterceptor())
val retrofit: Retrofit = builder
.client(httpClient.build()).build()
servicesApiInterface = retrofit.create(
ServicesApiInterface::class.java
)
return servicesApiInterface as ServicesApiInterface
}
private fun interceptor(): HttpLoggingInterceptor {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return httpLoggingInterceptor
}
interface ServicesApiInterface {
@GET("/services/rest/?method=flickr.photos.search")
fun getImageResults(
@Query("api_key") apiKey: String,
@Query("text") text: String,
@Query("format") format: String,
@Query("nojsoncallback") noJsonCallback: Boolean
): Call<PhotoResponse>
}
}
操作回调:
interface OperationCallback<T> {
fun onSuccess(data:List<T>?)
fun onError(error:String?)
}
照片数据源:
interface PhotoDataSource {
fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String)
fun cancel()
}
照片库:
class PhotoRepository : PhotoDataSource {
private var call: Call<PhotoResponse>? = null
private val API_KEY = "eff9XXXXXXXXXXXXX"
val FORMAT = "json"
companion object {
val TAG = PhotoRepository::class.java.simpleName
}
override fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String) {
call = ApiClient.build()
?.getImageResults(
apiKey = API_KEY,
text = searchText,
format = FORMAT,
noJsonCallback = true
)
call?.enqueue(object : Callback<PhotoResponse> {
override fun onFailure(call: Call<PhotoResponse>, t: Throwable) {
callback.onError(t.message)
}
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
Log.d(TAG, "got api response total pics are :${it.data?.size}")
if (response.isSuccessful && (it.isSuccess())) {
callback.onSuccess(it.data)
} else {
callback.onError(it.msg)
}
}
}
})
}
override fun cancel() {
call?.let {
it.cancel()
}
}
}
照片响应:
data class PhotoResponse(val status: Int?, val msg: String?, val data: List<Photo>?) {
fun isSuccess(): Boolean = (status == 200)
}
尝试更改您的 PhotoResponse
以匹配您的 json
响应。
data class PhotoResponse(
@SerializedName("stat")
val status: String?,
@SerializedName("photos")
val photos: Photos?
) {
fun isSuccess(): Boolean = status.equals("ok", true)
}
然后在onResponse
里面,你可以得到List<Photo>
如下:
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
//This should be your list of photos
it.photos.photos
}
}
问题出在您的数据 class 上。您在这里需要一份额外的数据 class。
因此,如果您仔细查看 JSON 的回复,您就会明白哪里出了问题。
您的照片数据 class 不应该是第一个 class。相反,它应该在另一个 class 里面,让我们说 PhotoApiResponse。
您的第一个 class 将包含照片和统计数据。
然后休息可以是一样的。
我成功命中 API 并获得 json 结果。我可以通过打印 Retrofit 响应正文在日志中看到成功结果。并且还使用 Stetho 作为网络拦截器。
但是,我无法理解为什么 api 响应在存储库的 onResponse()
方法中仍然是 "null"。我相信,我没有传递正确的模型可能是为了 JSON 被正确解析?谁能帮我找出这里的问题是什么?
以下是json:
{
"photos": {
"page": 1,
"pages": 2864,
"perpage": 100,
"total": "286373",
"photo": [
{
"id": "49570734898",
"owner": "165034061@N07",
"secret": "f3cb2c2590",
"server": "65535",
"farm": 66,
"title": "Hello",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
],
"photo": [
{
"id": "12344",
"owner": "23444@N07",
"secret": "f233edd",
"server": "65535",
"farm": 66,
"title": "Hey",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
]
},
"stat": "ok"
}
我的 Pojo Class :
data class Photos(
@SerializedName("page")
val page: Int,
@SerializedName("pages")
val pages: Int,
@SerializedName("perpage")
val perpage: Int,
@SerializedName("photo")
val photos: List<Photo>,
@SerializedName("total")
val total: String
)
data class Photo(
@SerializedName("farm")
val farm: Int,
@SerializedName("id")
val id: String,
@SerializedName("isfamily")
val isFamily: Int,
@SerializedName("isfriend")
val isFriend: Int,
@SerializedName("ispublic")
val isPublic: Int,
@SerializedName("owner")
val owner: String,
@SerializedName("secret")
val secret: String,
@SerializedName("server")
val server: String,
@SerializedName("title")
val title: String
)
改造客户端:
object ApiClient {
private val API_BASE_URL = "https://api.flickr.com/"
private var servicesApiInterface: ServicesApiInterface? = null
fun build(): ServicesApiInterface? {
val builder: Retrofit.Builder = Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.addInterceptor(interceptor()).addNetworkInterceptor(StethoInterceptor())
val retrofit: Retrofit = builder
.client(httpClient.build()).build()
servicesApiInterface = retrofit.create(
ServicesApiInterface::class.java
)
return servicesApiInterface as ServicesApiInterface
}
private fun interceptor(): HttpLoggingInterceptor {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return httpLoggingInterceptor
}
interface ServicesApiInterface {
@GET("/services/rest/?method=flickr.photos.search")
fun getImageResults(
@Query("api_key") apiKey: String,
@Query("text") text: String,
@Query("format") format: String,
@Query("nojsoncallback") noJsonCallback: Boolean
): Call<PhotoResponse>
}
}
操作回调:
interface OperationCallback<T> {
fun onSuccess(data:List<T>?)
fun onError(error:String?)
}
照片数据源:
interface PhotoDataSource {
fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String)
fun cancel()
}
照片库:
class PhotoRepository : PhotoDataSource {
private var call: Call<PhotoResponse>? = null
private val API_KEY = "eff9XXXXXXXXXXXXX"
val FORMAT = "json"
companion object {
val TAG = PhotoRepository::class.java.simpleName
}
override fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String) {
call = ApiClient.build()
?.getImageResults(
apiKey = API_KEY,
text = searchText,
format = FORMAT,
noJsonCallback = true
)
call?.enqueue(object : Callback<PhotoResponse> {
override fun onFailure(call: Call<PhotoResponse>, t: Throwable) {
callback.onError(t.message)
}
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
Log.d(TAG, "got api response total pics are :${it.data?.size}")
if (response.isSuccessful && (it.isSuccess())) {
callback.onSuccess(it.data)
} else {
callback.onError(it.msg)
}
}
}
})
}
override fun cancel() {
call?.let {
it.cancel()
}
}
}
照片响应:
data class PhotoResponse(val status: Int?, val msg: String?, val data: List<Photo>?) {
fun isSuccess(): Boolean = (status == 200)
}
尝试更改您的 PhotoResponse
以匹配您的 json
响应。
data class PhotoResponse(
@SerializedName("stat")
val status: String?,
@SerializedName("photos")
val photos: Photos?
) {
fun isSuccess(): Boolean = status.equals("ok", true)
}
然后在onResponse
里面,你可以得到List<Photo>
如下:
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
//This should be your list of photos
it.photos.photos
}
}
问题出在您的数据 class 上。您在这里需要一份额外的数据 class。 因此,如果您仔细查看 JSON 的回复,您就会明白哪里出了问题。 您的照片数据 class 不应该是第一个 class。相反,它应该在另一个 class 里面,让我们说 PhotoApiResponse。 您的第一个 class 将包含照片和统计数据。 然后休息可以是一样的。