when I start my application I keep getting this error. I don't know why am I getting this error because my Response class has list in constructor. I saw this link but I did not find an answer for my question. I am using json server for dummy RESTful api.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.social.network, PID: 3448
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:929)
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)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:929)
I/Process: Sending signal. PID: 3448 SIG: 9
This is my Api interface:
interface Api {
companion object {
const val BASE_URL = "http://be7c232bf30e.ngrok.io "
}
#GET("/posts")
suspend fun searchPhotos(
#Query("_page") page: Int,
#Query("_limit") perPage: Int
): Response
}
This is response data class:
data class Response(
val results: List<Post>
)
Probably you should either return list of Post or list of Response
interface Api {
companion object {
const val BASE_URL = "http://be7c232bf30e.ngrok.io "
}
#GET("/posts")
suspend fun searchPhotos(
#Query("_page") page: Int,
#Query("_limit") perPage: Int
): List<Post> // or List<Response>
}
Related
I'm developing an app that fetch exchange rate data for various currencies, but I'm facing an error after adding dynamic patameters to the value of GET Request. Here is my code:
interface CurrenciesApiService {
#GET("convert")
suspend fun getText(#Query("q") from_to: String, #Query("compact") compact: String, #Query("apiKey") key: String): Response<Currencies> {
return RetrofitInstances.retrofitService.getText(from_to, compact, key)
}
Retrofit Object:
object RetrofitInstances {
private val retrofit =
Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(
BASE_URL
).build()
val retrofitService: CurrenciesApiService by lazy { retrofit.create(CurrenciesApiService::class.java) }
}
BASE URL:
companion object {
const val BASE_URL = "https://free.currconv.com/api/v7/"
}
the Log:
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: TRY
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: USD
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: TRY_USD
2022-04-09 10:41:25.854 7036-7036/com.example.myapplication D/response: Response{protocol=h2, code=200, message=, url=https://free.currconv.com/api/v7/convert?q=TRY_USD&compact=ultra&apiKey=e0ba65b5ae1bf1b0019c}
and the last line is the query
The visual result:
Ok! I solved this issue by shifting to Scalar converter. I fetched the response as a string then I converted it to JSONObject, thus you can parse the value easily.
I want to do an async-await to my Retrofit request.
#GET("{companyId}/{filename}")
#Streaming
suspend fun getFilePart(
#Path(value = "companyId") companyId: Int,
#Path(value = "filename") filename: String,
#QueryMap queryMap: Map<String, String>
): Deferred<ResponseBody>
and when i call it from a CoroutineScope i have
val deferredList = pendingFiles.map {
async(Dispatchers.IO) {
try {
// runs in parallel in background thread
// Consider i have the data parameters....
apiManager.mApiService(base).getFilePart(it.companyId, fileName, urlQueryMap)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
So the request returns 200 which means that it is successful, and i see the Logging with the File part data. But i get the following exception
W/System.err: java.lang.RuntimeException: Unable to invoke no-args constructor for com.google.firebase.inject.Deferred<okhttp3.ResponseBody>. Registering an InstanceCreator with Gson for this type may fix this problem.
W/System.err: at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:228)
W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
W/System.err: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
W/System.err: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
W/System.err: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
W/System.err: at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
W/System.err: at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:520)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:920)
W/System.err: Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: com.google.firebase.inject.Deferred
W/System.err: at com.google.gson.internal.UnsafeAllocator.assertInstantiable(UnsafeAllocator.java:117)
W/System.err: at com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:49)
W/System.err: at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
W/System.err: ... 9 more
If your function is suspend, it shouldn't return Deferred. Just declare it this way:
#GET("{companyId}/{filename}")
#Streaming
suspend fun getFilePart(
#Path(value = "companyId") companyId: Int,
#Path(value = "filename") filename: String,
#QueryMap queryMap: Map<String, String>
): ResponseBody // no Deferred here
Functions with suspend keyword are used like regular functions (that's the beauty of coroutines), so they return a regular type. You actually start asynchronous code when you use coroutine builders like you do with async - this is the one that returns Deferred<T> and is not suspending.
I know there are some other topics on this problem but I couldn't find the solution for my error so if you have any suggestions, please let me know. I'm trying to create a POST request with a param in the body to retrieve some data from the API.
My DataApi interface:
interface ChartsDataApi {
#POST(NetworkUtils.CASH_COLLECTION_URL)
fun getCashCollection(#Body guid: String) : Call<List<CashCollection>>
#POST(NetworkUtils.TICKETS_DETAILS_URL)
fun getTicketsDetails() : Call<List<TicketDetails>>
}
My fun where I'm using Retrofit and where I'm trying to get the response:
private fun geCashCollectionDetails(){
var params : MutableMap<String, String> = HashMap()
val BASE_URL = " here i wrote the base URL "
val gson = GsonBuilder()
.setLenient()
.create()
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(ChartsDataApi::class.java)
GlobalScope.launch(Dispatchers.IO){
val response = api.getCashCollection(" **here i wrote the guid** ").awaitResponse()
if(response.isSuccessful){
val day = response.body()!!.lastIndex
Log.d("RAZZ", day.toString())
withContext(Dispatchers.Main){
}
}
}
My Data Class for CashCollection:
data class CashCollection(
#SerializedName("label")
val label: String?,
#SerializedName("value")
val value: String?)
Response from the server:
{"data":[{"label":"12.10.2020","value":"0,00"},{"label":"16.10.2020","value":"0,00"},{"label":"17.10.2020","value":"0,00"},{"label":"18.10.2020","value":"0,00"}],"currency":"EUR"}
Process: com.eschbachit.citylinemobile, PID: 32529
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:776)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:776)
When I'm trying to test and check the data I'm receiving, I get that error.
Please provide your data classes and response from server. This error means data from server can't be mapped into data classes that you provide as result of those requests.
Ok, data class must exactly reflect the structure of the response. Your CashCollection class must be
data class CashCollection(
val data: ArrayList<CashCollectionItem>,
val currency: String
) {
data class CashCollectionItem(
#SerializedName("label")
val label: String?,
#SerializedName("value")
val value: String?)
}
and type of response Call<CashCollection>
It appears you have incorrectly mapped your api response to kotlin classes. In your Retrofit api you say you expect the api to return a list of CashCollections ([{"label":"12.12.1212", "value": ""0,00}]), but the server returns a json object where that array is in the data property: {"data":[{"label":"12.12.1212", "value": "0,00"}]}
You should update your retrofit interface to correspond to what the api returns:
class CashCollectionResponse(
#SerializedName("data") data: List<CashCollection>
)
interface ChartsDataApi {
#POST("GetTheData")
fun getCashCollection(#Body guid: String): Call<CashCollectionResponse>
}
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)```
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...]
.
I have deployed a firebase cloud function (http function) which i could access successfully using postman tool, but when i try to access through client sdk from my android application i always get a permission denied error:
W/System.err: com.google.firebase.functions.FirebaseFunctionsException: PERMISSION_DENIED
04-19 15:18:00.258 3118-3118/com.abc.xyz W/System.err: at com.google.firebase.functions.FirebaseFunctions$2.onResponse(com.google.firebase:firebase-functions##16.3.0:273)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Here is the code i used to access the function
private fun saveInstallationId(key: String, userId : String, installationKey : String) : Task<String> {
val data = hashMapOf(
"key" to key,
"installationId" to installationKey,
"userId" to userId
)
return functions
.getHttpsCallable("setInstallationId")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
result
}
I think i am missing some headers, is it possible to set header parameters using client sdk?