Retrofit's onFailure method gets called even after right response - android

Following is my request parameters in PostMan
{"assign_id":"1","type":2,"attendance_list":[{"stud_id":"1703","attendanceID":"1","stud_attendance":"4"},{"stud_id":"1704","attendanceID":"2","stud_attendance":"1"},{"stud_id":"1705","attendanceID":"3","stud_attendance":"1"},{"stud_id":"1706","attendanceID":"4","stud_attendance":"1"},{"stud_id":"1707","attendanceID":"5","stud_attendance":"1"},{"stud_id":"1727","attendanceID":"25","stud_attendance":"1"}]}
Following is the response
{"status":1,"msg":"Success"}
Now in my Android App I am using Retrofit with Gson. But passing through Gson, I was facing some problem so I am sending request parameters in form of jsonObject and jsonArrays.
Following is my code when a button is pressed to submit request to server
val jObjRequest = JsonObject()
jObjRequest.addProperty("assign_id",ClassModelInstance.getInstance().classInfo.assignId)
jObjRequest.addProperty("type","2")
val attendanceArray = JsonArray()
for(i in 0 until ClassModelInstance.getInstance().studentInfos.size){
val jsonObject = JsonObject()
jsonObject.addProperty("stud_id",ClassModelInstance.getInstance().studentInfos[i].studId)
jsonObject.addProperty("attendanceID",1)
jsonObject.addProperty("stud_attendance",ClassModelInstance.getInstance().studentInfos[i].studAttendance)
attendanceArray.add(jsonObject)
}
jObjRequest.addProperty("attendance_list",attendanceArray.toString())
Log.i("PritishAttendanceApi2", jObjRequest.toString())
val submitAttendanceInterface = ApiClient.client.create(SubmitAttendanceInterface::class.java)
submitAttendanceInterface.takeAttendance(jObjRequest)
.enqueue(object : Callback<SubmitAttendanceResponse> {
override fun onFailure(call: Call<SubmitAttendanceResponse>, t: Throwable) {
activity?.let { it1 -> ToastMaker.make(it1,getString(R.string.something_went_wrong),Toast.LENGTH_LONG) }
Log.i("Pritish",t.message+"\t"+t.localizedMessage+"\t"+t.printStackTrace()+"\t"+t.cause+"\n"+call.request())
alertDialog.dismiss()
}
override fun onResponse(call: Call<SubmitAttendanceResponse>, response: Response<SubmitAttendanceResponse>) {
if(response.body()?.status.toString().equals("1",true)){
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
goToPreviousFragment()
} else {
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
}
alertDialog.dismiss()
}
})
This is the interface and response class
interface SubmitAttendanceInterface {
#Headers("Content-Type: application/json")
#POST("timetable/takeAttendance")
fun takeAttendance(#Body body: JsonObject): Call<SubmitAttendanceResponse>
}
data class SubmitAttendanceResponse(
#SerializedName("status")
#Expose
var status: Int? = null,
#SerializedName("msg")
#Expose
var msg: String? = null
)
When I log using HttpInterceptor I get com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 1 path
I searched Stack Overflow for the above error but the answers didn't met my requirement
JSON Error "java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"
"Expected BEGIN_OBJECT but was STRING at line 1 column 1"
I have edited the url in the logs as I don't want to expose the URL.

as per your log and sample data you should post data "attendance_list" as an json array insted of string
try
jObjRequest.add("attendance_list",attendanceArray)
insted of
jObjRequest.addProperty("attendance_list",attendanceArray.toString())

Related

Spotify API returns string as callback result instead of json

I am using Spotify API to login user to the app. this is the interface i wrote per documentation:
interface API {
#GET("/authorize")
fun login(#Query("client_id") client_id:String,
#Query("response_type") response_type:String,
#Query("redirect_uri")redirect_uri:String,
#Query("scope") scope:String
):Call<LoginResult>
This is the response result data class:
data class LoginResult(
val code: String
)
And this is the login function:
fun login() {
val BASE_URL = "https://accounts.spotify.com"
val CLIENT_ID = "c6c23e3e2f604f9aa1780fe7504e73c6"
val REDIRECT_URI = "com.example.myapp://callback"
val retrofit: Retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
val service: API = retrofit.create(API::class.java)
val listCall: Call<LoginResult> =
service.login(CLIENT_ID, "code", REDIRECT_URI, "user-top-read")
listCall.enqueue(object : Callback<LoginResult> {
override fun onResponse(response: Response<LoginResult>?, retrofit: Retrofit?) {
if (response?.body() != null) {
Log.i("result!", response.body().code)
}
if(response?.body() == null){
Log.i("Code" , response!!.code().toString())
Log.i("Response! ", "null response body")
}
}
override fun onFailure(t: Throwable?) {
Log.e("Here", "it is")
Log.e("Error", t!!.message.toString())
}
})
}
But I am getting this error:
E/Here: it is
E/Error: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 1 path $
There are a lot of questions here about this particular error and I read all of them and tried to implement the suggested solutions, but none worked.
Any help would be appreciated.
[this is the mentioned documentation link]
(https://developer.spotify.com/documentation/general/guides/authorization/code-flow/)

Android generic Gson.fromJson() conversion with coroutine and flow

I am making generic classes for hitting otp api.anybody can use otp section just have to pass request ,Response class and url and all will be done by this otp section.
Please note : this response class can be of different type (for eg: MobileOtpResponse,EmailOtpResponse)
below is the generic OtpClient which takes any request type and returns particular passed ResponseType (for example : Request class passed is OtpRequest ,ResponseType class passed is OtpResponse)
interface OtpClient {
#POST
suspend fun <Request : Any, ResponseType> sendOtp(#Url url: String,
#Body request:#JvmSuppressWildcards Any): #JvmSuppressWildcards ResponseType
}
OtpRequest
data class OtpRequest(#SerializedName("mobile_number") val mobileNumber: String,#SerializedName("app_version") val appVersion: String)
OtpResponse
data class OtpResponse(#SerializedName("status") val status: String = "",
#SerializedName("response") val response: OtpData? = null)
data class OtpData(
#SerializedName("otp_status") val otpStatus: Boolean = false,
#SerializedName("message") val message: String = "",
#SerializedName("error") val error: Int? = null,
#SerializedName("otp_length") val otpLength: Int? = null,
#SerializedName("retry_left") val retryLeft: Int? = null,)
Now i create Repo to call this api this simply use flow and when the data fetch it emits the data
class OtpRepoImpl<out Client : OtpClient>(val client: Client) :OtpRepo {
override fun <Request:Any, ResponseType> sentOtpApi(url: String, request: Request): Flow<ResponseType> {
return flow<ResponseType> {
// exectute API call and map to UI object
val otpResponse = client.sendOtp<Request, ResponseType>(url,request)
emit(otpResponse)
}.flowOn(Dispatchers.IO) // Use the IO thread for this Flow
}
}
this repo is used in viewmodel class
#ExperimentalCoroutinesApi
fun <A : Class<ResponseType>, Request : Any, ResponseType : Any> sendOtp(a: Class<ResponseType>, request: Request, response: ResponseType, url: String) {
viewModelScope.launch {
repo.sentOtpApi<Request, ResponseType>(url, request = request)
.onStart { _uiState.value = OtpState.Loading(true) }
.catch { cause ->
_uiState.value = OtpState.Loading(false)
getResponseFromError<Class<ResponseType>,ResponseType>(cause, response) {
// emit(it)
}
}
.collect {
_uiState.value = OtpState.Loading(false)
_uiState.value = OtpState.Success(it)
}
}
}
as you can see above this sendOtp method is called from the view class and inside this method we use repo.sentOtpApi and pass generic request response type.I get data in catch block coz api is send error otp data in 400 HttpException so i created another method getResponseFromError to get error response it should parse the errorBody response and call this lambda block.
private suspend fun <A : Class<*>, ResponseType : Any> getResponseFromError( cause: Throwable, rp: ResponseType, block: suspend (ResponseType) -> Unit) {
if (cause is HttpException) {
val response = cause.response()
if (response?.code() == 400) {
println("fetching error Response")
val errorResponse = response.errorBody()?.charStream()
val turnsType = object : TypeToken<ResponseType>() {}.type
val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
block(finalErrorResponse)
} else {
println("someOther exception")
}
} else
_uiState.value = OtpState.Error(cause)
}
so here i am facing the problem inside above method
val turnsType = object : TypeToken<ResponseType>() {}.type
val finalErrorResponse = Gson().fromJson<ResponseType>(errorResponse, turnsType)
block(finalErrorResponse)
This finalErrorResponse is returning LinkedTreeMap instead of ResponseType (in this case its OtpResponse)
i have also tried using Class<*> type like this
val turnsType = object : TypeToken<A>() {}.type
val finalErrorResponse = Gson().fromJson<A>(errorResponse, turnsType)
but its not working.
calling of this sentOtp viewmodel func is like
var classType = OtpResponse::class.java
otpViewModel.sendOtp(a = classType, request = otpRequest, response = OtpResponse() , url =
"http://preprod-api.nykaa.com/user/otp/v2/send-wallet-otp")
[![value in finalErroResponse][1]][1]
[1]: https://i.stack.imgur.com/Holui.png
required: finalErroResponse should be of OtpResponse type because that was passed in sentOtp func
Please help :)

retrofit error Expected BEGIN_ARRAY but was BEGIN_OBJECT

I try to get Otp using mobile number, but it display error like this
E/FAILISJERE: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 54 path $.data
This is my URL := http://192.168.1.105/XXXX/XXXXX/XXXXX/default/send-otp
Request Fields: mobileNo,name
Response is like this :-
{
"error": false,
"msg": "Otp sent successfully",
"data": {
"otp": 152265
}
}
APIClient.Kt:-
object ApiClient {
private var retrofit: Retrofit? = null
val client: Retrofit
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(AppConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit!!
}
}
APIInterface.kt:-
interface ApiInterface {
#FormUrlEncoded
#POST("send-otp")
fun GET_OTP(#Field("name") name: String, #Field("mobileNo") mobileNo: String): Call<OTPSendResponse>
}
AppConfig.kt:-
class AppConfig {
companion object {
const val BASE_URL = "http://192.168.1.105/XXXX/XXXXX/XXXXX/default/"
}
}
OtpModel.kt:-
class OtpModel {
constructor(otp: Int) {
this.otp = otp
}
#SerializedName("otp")
var otp: Int = 0
}
OtpSendResponse.kt:-
class OTPSendResponse {
constructor(error: String, data: ArrayList<OtpModel>, msg: String) {
this.error = error
this.data = data
this.msg = msg
}
#SerializedName("error")
var error: String = ""
#SerializedName("msg")
var msg: String = ""
#SerializedName("data")
var data: ArrayList<OtpModel> = ArrayList()
}
MyActivity.kt:-
private fun sendNameAndMobileNum(name: String, mobileNum: String) {
Log.e("MOBILE", "${mobileNum}")
val apiService = ApiClient.client.create(ApiInterface::class.java)
val call = apiService.GET_OTP(name, mobileNum)
call.enqueue(object : Callback<OTPSendResponse> {
override fun onResponse(call: Call<OTPSendResponse>, response: Response<OTPSendResponse>) {
Log.e("OTP", "${response.body()?.data!![0].otp}")
val otpIs = response.body()!!.data[0].otp
val i = Intent(this#AddNumActivity, OTPVerifyActivity::class.java)
i.putExtra("otp", otpIs)
i.putExtra("mobileNum", mobileNum)
startActivity(i)
}
override fun onFailure(call: Call<OTPSendResponse>, t: Throwable) {
Toast.makeText(this#AddNumActivity, "Ooops !!", Toast.LENGTH_SHORT).show()
Log.e("FAILISJERE", "${t.message}")
}
})
}
Change Model class because in json response there are not any array so remove ArrayList tag
data: ArrayList<OtpModel>
to
data: OtpModel
because it's no array
Your error means that the conversion from what you received from the API to the class you provided in your call is not correct.
E/FAILISJERE: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 54 path $.data
Means the deserializer expected an array but has found a "{" character at the line 1 column 54 (which corresponds to the data field) instead of the '[' it was expected for the "data" field. So that means your model is not correct.
And if you look at your model, you can indeed see that the "data" object here is represented as an ArrayList, while it should be a single object.
So you can just replace in your model
data: ArrayList<OtpModel>
by:
data: OtpModel
and you should be good

Fuel Expected Begin Array but was Begin Object

I'm new to Kotlin and I'm still learning how to get an object respose but I'm having the following exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Here's the Json Result:
{"Success":"1","Message":"True","Items":[{"Id":3,"Name":"AndroidVersion","Value":"1"},{"Id":4,"Name":"IOSVersion","Value":"1.0"},{"Id":5,"Name":"AndroidForceUpdate","Value":"true"}]}
Here's the model class
data class MobileConfiguration(
val id: Int,
val name: String,
val value: String
) {
class Deserializer : ResponseDeserializable<Array<MobileConfiguration>> {
override fun deserialize(content: String): Array<MobileConfiguration>? = Gson().fromJson(content, Array<MobileConfiguration>::class.java)
}
}
And here's the Call:
url.httpPost(listOf(Pair("key", ""))).responseObject(MobileConfiguration.Deserializer()) { request, response, result ->
//val (people, err) = result.get()
when (result) {
is Result.Failure -> {
val ex = result.getException()
Log.wtf("ex", "is " + ex)
}
is Result.Success -> {
val (config, err) = result
//Add to ArrayList
config?.forEach { configuration ->
mobileConfigurations.add(configuration)
}
println(mobileConfigurations)
}
}
}
What Am i doing wrong please if anybody can help
I think the problem is in the model class.
You assume that the MobileConfiguration array is directly under the response class, however, actually, it is under the Items object.
So instead of using Array<MobileConfiguration> as a responseObject, you should use other class as a container like:
data class MobileConfigurationResponse(
val success: String,
val message: String,
val items: Array<MobileConfiguration>
)
and then you can get the array like this:
val (config, err) = result.items

How to get Json Response in Arraylist from List<Item> using RxJava and Kotlin

Facing Problem on getting Response in ArrayList.
I have following Respose on String value
var res_message: String = ""
res_message = "${result.vehicletypes} "
Getting below Value on this String
[VehicleType(_id=1, vehicleType=Hatchback, __v=0),
VehicleType(_id=2, vehicleType=Maruti, __v=0),
VehicleType(_id=3, vehicleType=Honda, __v=0),
VehicleType(_id=4, vehicleType=Bike, __v=0)]
Retrofit Result is
vehicletypes = {ArrayList#6055} size = 4
0 = {Model$VehicleType#6058} "VehicleType(_id=1,
vehicleType=Hatchback, __v=0)"
1 = {Model$VehicleType#6059} "VehicleType(_id=2,
vehicleType=Maruti, __v=0)"
2 = {Model$VehicleType#6060} "VehicleType(_id=3,
vehicleType=Honda, __v=0)"
3 = {Model$VehicleType#6061} "VehicleType(_id=4,
vehicleType=Bike, __v=0)"
Below Code snippest sending request to API.
disposable = apiServices.getVehicle(token)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
res_message = "${result.vehicletypes} "
Log.d("Type==", res_message)
},
{ error ->
res_message = "${error.message}"
// validateToken()
}
)
Model Class
data class Vehicles(val success: Boolean, val vehicletypes: List<VehicleType>, val message: String)
data class VehicleType(val _id: String, val vehicleType: String, val __v: String)
I want to get this value on Arralist VehicleType List on below vehicleListArray
private var vehicleListArray: ArrayList<Model.VehicleType>? = null
How we can achieve this.
Thanks in advance.
Assuming that what you are trying to parse is a response from a service that is able to send you propper format for lists (eg Json) than
Retrofit can handle parsing lists with ease.
In your apiService definition:
fun getPeople(token: Whatever): Observable<List<VehicleType>>
And if you don't have it already:
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
I got solution I have to handle Respose as below code snippet.
private fun getVehicleType() {
disposable?.add(apiServices.getVehicle(token)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError))
}
private fun handleResponse(vehicles: Model.Vehicles) {
VehiclesArrayList = ArrayList(vehicles.vehicletypes)
Log.d("type==","n--"+VehiclesArrayList )
mAdapter = DataAdapter(VehiclesArrayList !!, this)
v_android_list.adapter = mAdapter
}
private fun handleError(error: Throwable) {
Log.d("type", error.localizedMessage)
Toast.makeText(context, "Error ${error.localizedMessage}", Toast.LENGTH_SHORT).show()
}

Categories

Resources