Невозможно проанализировать JSON с помощью Retrofit в Android - PullRequest
0 голосов
/ 23 февраля 2020

Я успешно могу использовать API и получить результат json. Я могу увидеть результат успеха в журналах, напечатав ответное тело Retrofit. а также использование Stetho в качестве сетевого перехватчика.

Однако я не могу понять, почему ответ API все еще "нулевой" в методе onResponse() в хранилище. Я полагаю, я не передаю правильную модель, может быть, 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:

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
)

RetrofitClient:

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?)
}

PhotoDataSource:

interface PhotoDataSource {

    fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String)
    fun cancel()
}

PhotoRepository:

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()
        }
    }
}

PhotoResponse:

data class PhotoResponse(val status: Int?, val msg: String?, val data: List<Photo>?) {
    fun isSuccess(): Boolean = (status == 200)
}

Ответы [ 2 ]

2 голосов
/ 23 февраля 2020

Попробуйте изменить 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
    }
}
0 голосов
/ 23 февраля 2020

Проблема связана с вашим классом данных. Вам нужен один дополнительный класс данных здесь. Поэтому, если вы внимательно посмотрите на свой ответ JSON, то поймете, что происходит не так. Ваш класс данных фотографий не должен быть первым классом. Вместо этого он должен быть внутри еще одного класса, скажем, PhotoApiResponse. Ваш первый класс будет содержать как фотографии, так и статистику. И тогда отдых может быть таким же.

...