Sir Isaac
Sir Isaac

Reputation: 117

JsonArray to Kotlin data class using Retrofit and GsonConverterFactory(Expected BEGIN_OBJECT but was BEGIN_ARRAY)

I'm trying to load quotesJson file from github(https://github.com/JamesFT/Database-Quotes-JSON/blob/master/quotes.json). I'm new to retrofit and all of this, so I just tried following and understanding a tutorial(https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777) . Incredibly sorry if this is stupid or really easy. I'm still struggling with this. If i can get a short explanation of why it's done some other way I'd appreciate it!

I've looked at documentation for Retrofit, searched all of the similar overflow questions. The problem is, if I try to change fun getQuotes(): Deferred<Response<QuoteResponse>> to fun getQuotes(): Deferred<ResponseList<Quotes>> I get an error in val quoteResponse = safeApiCall(...

    private val okHttpClient = OkHttpClient().newBuilder()
        .build()

    fun retrofit() : Retrofit = Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl("https://raw.githubusercontent.com/JamesFT/Database-Quotes-JSON/master/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .build()


    val quoteApi : QuoteApi = retrofit().create(QuoteApi::class.java)

}

model

   val quoteAuthor : String,
   val quoteText : String
)

// Data Model for the Response returned from the Api
data class QuoteResponse(
    val results : List<Quote>
)

//A retrofit Network Interface for the Api
interface QuoteApi{
    @GET("quotes.json")
    fun getQuotes(): Deferred<Response<QuoteResponse>>
    // fun getQuotes(): Deferred<Response<QuoteResponse>>
}
val quoteResponse = safeApiCall(
            call = {api.getQuotes().await()}, // i get the error here if i change something myself
            errorMessage = "Error Fetching Popular Movies"
        )

        return quoteResponse?.results?.toMutableList()

    }
suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>, errorMessage: String): T? {

        val result : Result<T> = safeApiResult(call,errorMessage)
        var data : T? = null

        when(result) {
            is Result.Success ->
                data = result.data
            is Result.Error -> {
                Log.d("1.DataRepository", "$errorMessage & Exception - ${result.exception}")
            }
        }


        return data

    }
    Process: com.example.quoteappmvvm, PID: 7373
    com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
...

Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
        at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)```

Upvotes: 1

Views: 4449

Answers (1)

Lazar
Lazar

Reputation: 690

The problem you are facing is because the response is an array but you are trying to handle it as an object.

This is how your QuoteApi should look like

interface QuoteApi{
    @GET("quotes.json")
    fun getQuotes(): Deferred<Response<List<Quote>>>
}

Explanation: To explain it a little better for you - your current expected response is

data class QuoteResponse(
    val results : List<Quote>
)

and Retrofit and Gson expect that your JSON response looks like

{
  "results": [...here would be your Qoute objects...]
}

when actually the response is only an array

[...here are Quote objects...]

.

Upvotes: 6

Related Questions