I have parse the data from this link
https://api.androidhive.info/contacts/
But I am getting error as
E/onĀ FailureĀ :: retrofit errorjava.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Below is the code which I done.
class RetrofitService {
val liveUserResponse:MutableLiveData<List<ContactBase>> = MutableLiveData()
companion object Factory {
var gson = GsonBuilder().setLenient().create()
fun create(): ApiInterface {
Log.e("retrofit","create")
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("https://api.androidhive.info/")
.build()
return retrofit.create(ApiInterface::class.java)
}
}
fun loadContactsData(): MutableLiveData<List<ContactBase>>? {
Log.e("loadAndroidData","yes")
val retrofitCall = create().getContacts()
retrofitCall.enqueue(object : Callback<List<ContactBase>> {
override fun onFailure(call: Call<List<ContactBase>>, t: Throwable?) {
Log.e("on Failure :", "retrofit error"+t)
Log.e("on Failure :", "retrofit error"+call)
}
override fun onResponse(call: Call<List<ContactBase>>, response: retrofit2.Response<List<ContactBase>>) {
val list = response.body()
for (i in list.orEmpty()){
Log.e("on response 1:", ""+i)
}
liveUserResponse.value = list
Log.e("hasActiveObservers 1", liveUserResponse.hasActiveObservers().toString()+" check")
Log.e("on response 2 :", liveUserResponse.toString()+" check")
}
})
return liveUserResponse
}
}
But it's always going to Failure state.
data class ContactBase (val contacts : List<Contacts>)
data class Contacts (
val id : String,
val name : String,
val email : String,
val address : String,
val gender : String,
val phone : Phone
)
data class Phone (
val mobile : String,
val home : String,
val office : String
)
interface ApiInterface{
#GET("contacts/")
fun getContacts(): Call<List<ContactBase>>
}
class AndroidViewModel:ViewModel(){
private val retrofitService = RetrofitService()
fun getContactsData(): MutableLiveData<List<ContactBase>>?{
return retrofitService.loadContactsData()
}
}
I cross verified the url too and pojo class. But it always go to failure case in retrofit.
The error says that the incomming JSON starts with a { and not with a [ which means it's an object and not an array of objects.
So you should be having a class that has an array of Contact in order to make that call successful.
A small heads up: Since you are using GSON, your model classes would need the implementation of #SerializedName(string) annotation above the variables.
Related
i am new to kotlin and i am in learning phase. I have followed many links but didn't able to understand completely.
I want Json response to show in my textview.
Problem: 1
I have tried this code but was unable to get data, but i want to get the items inside data object. Quote and author are coming null.
{
"status": 200,
"message": "Success",
"data": {
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
},
"time": "0.14 s"
}
Problem: 2
I dont know how to parse this response in textview
object ServiceBuilder {
private val client = OkHttpClient.Builder().build()
private val retrofit = Retrofit.Builder()
.baseUrl("https://url.com.pk/") // change this IP for testing by your actual machine IP
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}}
RestApi
interface RestApi{
#Headers("Content-Type: application/json")
#POST("api/getquotes")
abstract fun addUser(#Body userData: UserInfo): Call<UserInfo>}
RestAPiService
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<UserInfo>
{
override fun onFailure(call: Call<UserInfo>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<UserInfo>, response: Response<UserInfo>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser)
}
}
)
}
}
UserInfo
data class UserInfo (
#SerializedName("Quote")
val quote : String,
#SerializedName("Author")
val author : String
)
MainActivity
fun getQuotes() {
val apiService = RestApiService()
val userInfo = UserInfo("","")
apiService.addUser(userInfo) {
Log.d("Error registering user","errter")
/*if ( != null)
{
// it = newly added user parsed as response
// it?.id = newly added user ID
} else {
Log.d("Error registering user","errter")
}*/
}
}
Any help would be appreciated :)
Status, message and data are all part of the response so you need to take care of that. For example this
data class AddUserResponse(
val `data`: UserInfo, //like you defined it
val message: String,
val status: Int,
val time: String
)
This means parameter and response are different so the RestApi needs to be changed to this
abstract fun addUser(#Body userData: UserInfo): Call<AddUserResponse>}
This in turn also change the types in the service like
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<AddUserResponse>
{
override fun onFailure(call: Call<AddUserResponse>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<AddUserResponse>, response: Response<AddUserResponse>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser.data)
}
}
)
}
}
now in getQuotes you will have that it is a UserInfo object
apiService.addUser(userInfo) {
val returnedUserInfo = it
}
just follow my steps :
File->settings->Plugins
search for JSON To Kotlin class and install it
again click on File->New->Kotlin Data class from JSON
paste your json code here and click on generate. It will generate POJO classes and you will good to go.
The first thing I noticed, is that the data in your json is:
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
While your UserInfo defined #SerializedName("message") for Quote.
I'm getting this error ** Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $**
gradles below which I used for retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
Retrofit class
class RetrofitClient private constructor() {
val myApi: Api
companion object {
#get:Synchronized
var instance: RetrofitClient? = null
get() {
if (field == null) {
field = RetrofitClient()
}
return field
}
private set
}
init {
val gson = GsonBuilder()
.setLenient()
.create()
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client : OkHttpClient = OkHttpClient.Builder().addInterceptor(interceptor).build()
val retrofit: Retrofit = Retrofit.Builder().baseUrl("http://ctyf.co.in/api/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
myApi = retrofit.create(Api::class.java)
}
}
Api interface
public interface Api {
#Headers("Content-Type: application/json")
#GET("companyvehiclelatestinfo?token=F934A0C439/")
fun getLatLngs(): Call<ResponseLoc?>?
}
data class
data class ResponseLoc(
val Vehicle: List<Vehicle>
)
data model
data class Vehicle(
val Angle: String,
val Date: String,
val Ignition: String,
val Imei: String,
val Lat: String,
val Location: String,
val Long: String,
val Speed: String,
val Tempr: String,
val VehicleNo: String
)
finally calling here
private fun getLatLngs() {
val call: Call<ResponseLoc?>? = RetrofitClient.instance!!.myApi.getLatLngs()
call!!.enqueue(object : Callback<ResponseLoc?> {
override fun onResponse(call: Call<ResponseLoc?>, response: Response<ResponseLoc?>) {
val responseLoc: List<ResponseLoc> = response.body() as List<ResponseLoc>
//Creating an String array for the ListView
val data = arrayOfNulls<String>(responseLoc.size)
for (i in responseLoc.indices) {
data[i] = responseLoc[i].Vehicle.toString()
Log.d("apiii", data[i].toString())
}
}
override fun onFailure(call: Call<ResponseLoc?>, t: Throwable) {
Log.d("apii", t.message.toString())
}
})
}
JSON values
{"Vehicle":[{"VehicleNo":"Test","Imei":"354019","Location":"Tamil Nadu-India","Date":"2021-03-17 19:27:12.000","Tempr":"0","Ignition":"","Lat":"13.11","Long":"80.282","Speed":"0","Angle":"0"}]}
I have tried many stacks none of them helped
Is there any other ways available except Retrofit ???
anybody please help me to get the api results
I have tried many stacks none of them helped
Is there any other ways available except Retrofit ???
anybody please help me to get the api results
I am working on an android project. I use Kotlin and I'm just learning it. I want to use Retrofit 2 for my request. I use Java this method and it is done.
I want to register user my database. When I use my function, return success for web services but it saves the empty value and I take this error: "Expected BEGIN_OBJECT was string at line 3 column 1 path$". How we can solve this problem? Actually, I read and implementing other solutions but they don't work for me.
My API:
#POST("userregister.php")
fun doRegister(
#Body signupRequest: SignupRequest
): Call<SignupResponse> // body data
My API Service:
object ApiServiceWithOutRX {
private const val BASE_URL = "https://alperenyukselaltug.com/api/TurkAi/"
var gson = GsonBuilder()
.setLenient()
.create()
fun ApiCall() = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(ApiWorker.client)
.build()
.create(APIListWithOutRX::class.java)!!
}
And my register function:
val email = editTextMail!!.text.toString().trim()
val password = editTextPassword!!.text.toString().trim()
val json = JSONObject()
json.put("UserEmail", email)
json.put("UserPassword", password)
json.put("UserProfilePicture", "")
ApiServiceWithOutRX.ApiCall().doRegister(
SignupRequest(
email,
password,
""
)
).enqueue(object : Callback<SignupResponse> {
override fun onResponse(
call: Call<SignupResponse>,
response: Response<SignupResponse>
) {
Log.d("Response::::", response.body().toString())
val loginResponse: SignupResponse
loginResponse = response.body()!!
if (loginResponse.status) {
finish()
} else {
Toast.makeText(applicationContext, response.body()!!.message, Toast.LENGTH_LONG)
.show()
}
}
override fun onFailure(call: Call<SignupResponse>, t: Throwable) {
Toast.makeText(applicationContext, t.message, Toast.LENGTH_LONG).show()
}
})
My Model:
data class User(
#SerializedName("UserEmail")
val UserEmail: String?,
#SerializedName("UserPassword")
val UserPassword: String?,
#SerializedName("UserProfilePicture")
val UserProfilePicture: String?
)
class SignupResponse(val status: Boolean, val message:String, val data: User)
class SignupRequest(#SerializedName("UserEmail") var UserEmail: String,
#SerializedName("UserPassword") var UserPassword: String,
#SerializedName("UserProfilePicture") var UserProfilePicture: String)
I solve this problem. First, I return my server side a string with PHP in echo. I return a string value. So I change my #Post like this:
#FormUrlEncoded
#POST("userregister.php")
fun doRegister(
#Field("UserEmail") UserEmail:String,
#Field("UserPassword") UserPassword:String,
#Field("UserProfilePicture") UserProfilePicture:String
):Call<String>
help me for this issue please, I want to get data from API
{
"status": true,
"data": [
{
"id_pelanggan": "456",
"nama_pelanggan": "ahmad",
"alamat": "taliwang"
},
{
"id_pelanggan": "457",
"nama_pelanggan": "ahmad",
"alamat": "taliwang"
}
]}
this is my API object for setup for dynamic class, retrofit2 and gson
object Api {
private val BASE_URL: String = BuildConfig.API_SRAPP
private var gson = GsonBuilder().setLenient().create()
private val httpClient = OkHttpClient.Builder()
fun <T>service(java: Class<T>): T{
val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(BASE_URL)
.client(httpClient.build())
.build()
return retrofit.create(java)
}
}
this is my class BaseRespon for handle all data from API and with dynamic class
data class BaseResponse<T>(
val status: Boolean,
val data: T?
)
this is my class Customer
data class Customer (
#SerializedName("id_pelanggan")
#Expose
val idPelanggan: String,
#SerializedName("nama_pelanggan")
#Expose
val namaPelanggan: String,
#SerializedName("alamat")
#Expose
val alamat: String
)
this is my API service
interface CustomerServices {
#GET("customer")
fun getAllCustomer(#Header("Authorization") auth: String): Call<BaseResponse<ArrayList<Customer>>>
#GET("customer")
fun getCustomerbyID(#Header("Authorization") auth: String, #Query("id") id: String): Call<BaseResponse<Customer>>
}
and this is my class for using API
class CustomerPresenter {
fun loadAllCustomer(){
apiCustomer.getAllCustomer(OfflineHelper.getToken())
.enqueue(object : Callback<BaseResponse<ArrayList<Customer>>>{
override fun onFailure(call: Call<BaseResponse<ArrayList<Customer>>>, t: Throwable) {
Log.e("allCustomer", "${t.message}")
}
override fun onResponse(
call: Call<BaseResponse<ArrayList<Customer>>>,
response: Response<BaseResponse<ArrayList<Customer>>>
) {
saveLocalCustomer(response.body())
}
})
}
fun saveLocalCustomer(data: BaseResponse<ArrayList<Customer>>?){
Log.w("loadedAll", "${data?.status}")
}
and I have log failure in loadAllCustomer
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 1 path $
I don't understand why it's failure, my endpoint is not typo, and I have tested it on postman
You need to use a List as your return type for the data field. try this:
data class BaseResponseList<T>(
val status: Boolean,
val data: List<T>?
)
and then using BaseResponseList in your call as following:
#GET("customer")
fun getAllCustomer(#Header("Authorization") auth: String): Call<BaseResponseList<Customer>>
Use BaseResponseList for any endpoint that returns a List, and BaseResponse for Objects
Update your response class and api interface like below
data class BaseResponse(
val status: Boolean,
val data: List<Customer>?
)
interface CustomerServices {
#GET("customer")
fun getAllCustomer(#Header("Authorization") auth: String): Call<BaseResponse>
#GET("customer")
fun getCustomerbyID(#Header("Authorization") auth: String, #Query("id") id: String): Call<Customer>
}
your model not same with you object json
I have a app whos call a service POST (postman test) and get some information from that call.
My retrofit initializer:
class RetrofitInitializer {
private val retrofit = Retrofit.Builder()
.baseUrl("https://bank-app-test.herokuapp.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
fun loginApiContract() : LoginApiContract{
return retrofit.create(LoginApiContract::class.java)
}
}
the interface:
interface LoginApiContract {
#POST("login")
fun login() : Call<UserAccount>
#GET("statements")
fun getStatements()
}
and finally the call:
val call = RetrofitInitializer().loginApiContract().login()
call.enqueue(object: Callback<UserAccount> {
override fun onResponse(call: Call<UserAccount?>?,
response: Response<UserAccount?>?) {
response?.body()?.let {
val myUserAccount: UserAccount = it
loginView.doLogin(myUserAccount)
}
}
override fun onFailure(call: Call<UserAccount?>?,
t: Throwable?) {
Log.e("onFailure error", t?.message)
}
})
I got response code 200, but response body is empty.
This is my postman response:
{
"userAccount": {
"userId": 1,
"name": "Jose da Silva Teste",
"bankAccount": "2050",
"agency": "012314564",
"balance": 3.3445
},
"error": {}
}
and this is my model:
class UserAccount constructor(var userId: Int, var name: String, var bankAccount: String, var agency: String, var balance: Double){
init{
this.userId = userId
this.name = name
this.bankAccount = bankAccount
this.agency = agency
this.balance = balance
}
}
I found!
Its a problem with my postman. For any reason my AVD cant access the mock server. now i solve my problem with a simple restful api node.js.
Thx for the help guys.
Your response contains elements named "userAccount" and "error". Your UserAccount class has neither which is causing the issue. Therefore, use Retrofit with a class like this:
data class UserResponse(val userAccount: UserAccount, val error: BackendError)