Can't getting data from API using retrofit2 - android

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

Related

How to Parse Kotlin Units with Moshi

I'm using retrofit for web requests and then moshi for JSON parsing,this is api
#POST("/verify_code/send_email")
suspend fun sendEmail(#Body sendEmailRequest: SendEmailRequest): BaseResponse<Unit>
the BaseResponse
#JsonClass(generateAdapter = true)
open class BaseResponse<T> {
#Json(name = "code")
var code: Int? = null
#Json(name = "message")
var message: String? = null
#Json(name = "data")
var data: T? = null
}
JSON String
{
"code": 200,
"message": "Some Message",
"data": null
}
and error log
2021-11-26 09:59:24.166 14288-14288/com.gow E/FlowKtxKt$next: java.lang.IllegalArgumentException: Unable to create converter for com.gow.base.BaseResponse<kotlin.Unit>
for method VerifyApi.sendEmail
I tried adding the following, but it didn't work
object UnitConverterFactory : Converter.Factory() {
override fun responseBodyConverter(
type: Type, annotations: Array<out Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
return if (type == Unit::class.java) UnitConverter else null
}
private object UnitConverter : Converter<ResponseBody, Unit> {
override fun convert(value: ResponseBody) {
value.close()
}
}
}
it`s the Moshi bug.
I solved my problem by using Any instead of Unit.
like this:
#POST("/verify_code/send_email")
suspend fun sendEmail(#Body sendEmailRequest: SendEmailRequest): BaseResponse<Any>
I had the same issue.
I fixed it by following the comment on this link.
I do not see how you are adding your UnitConverterFactory but in my case, the order you add it is important.
I am also using MoshiConverterFactory and ApiResultConverterFactory from EitherNet, so my UnitConverterFactory had to be placed after ApiResultConverterFactory and before MoshiConverterFactory:
Retrofit.Builder()
...
.addCallAdapterFactory(ApiResultCallAdapterFactory)
//the order of the converters matters.
.addConverterFactory(ApiResultConverterFactory)
.addConverterFactory(UnitConverterFactory)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()

Kotlin Retrofit Error: "Expected BEGIN_OBJECT but was string"

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>

How to test api requests and fill classes with fake data?

I can not find a solution to this problem on the Internet. Or is my code so bad?
Interface
interface GetWeatherService {
#GET("/data/2.5/forecast?")
fun getAllWeather(#Query("q")cityName: String, #Query("APPID")app_id: String, #Query("units")units: String="imperial"): Call<ListWeather>
#GET("/data/2.5/weather?")
fun getCurrentWeather(#Query("q")cityName: String, #Query("APPID")app_id: String, #Query("units")units: String="imperial"): Call<MainWeather>
#GET("/data/2.5/weather?")
fun getWeatherDataFromLocation(#Query("lat")lat: String, #Query("lon")lon: String, #Query("APPID") app_id: String): Call<DataFromLocation>
}
Client
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private var BASE_URL = "https://api.openweathermap.org/"
val retrofitInstance: Retrofit?
get(){
if(retrofit == null){
retrofit = retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
MainActivity()
class MainActivity: AppCompatActivity {
fun getDataFromApi(cityName: String) {
val service = RetrofitClientInstance.retrofitInstance!!.create(GetWeatherService::class.java)
// Service 1
service.getCurrentWeather(cityName, APP_ID).enqueue(object: Callback<MainWeather>{
override fun onFailure(call: Call<MainWeather>, t: Throwable) {
}
override fun onResponse(call: Call<MainWeather>, response: Response<MainWeather>) {
val weather = response.body()
val currentWeather = CurrentWeather(
weather!!.main.temp,
weather.weather[0].description,
weather.name,
weather.weather[0].main
)
updateCurrentWeatherUI(currentWeather)
}
})
service.getAllWeather(cityName, APP_ID).enqueue(object: Callback<ListWeather>{
override fun onFailure(call: Call<ListWeather>, t: Throwable) {
}
override fun onResponse(call: Call<ListWeather>, response: Response<ListWeather>) {
val weather = response.body()!!.list
for(item in weather){
val weatherList = NextFiveDayWeather(
item.dt,
item.main.temp,
item.weather[0].description
)
weatherArray.add(weatherList)
updateUI(weatherArray)
}
}
})
}
}
Data class
data class MainWeather (
var dt: Long,
var main: MainDTO,
var weather: List<WeatherDTO>,
var dt_txt: String,
var name: String
)
data class WeatherDTO (var main: String, var description: String, var icon: String)
data class ListWeather (#SerializedName("list") var list: List<MainWeather>)
I can’t test the request to api and I can’t fill the classes with fake data?
Tell me please, what should I do?
What should I study? And if you are not difficult can give advice for the future?
You can download the software Postman to test api endpoints: https://www.getpostman.com/

How to fix retrofit errorjava.lang.IllegalStateException android

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.

Retrofit 2 request always failure - Kotlin

I tried to learn about kotlin and retrofit 2 at the same time. I have this code.
I want to take all the /posts from this. But its always return a failure code. I'm very new in this, thanks
Network interface
interface APIService {
#GET("/posts")
fun getPosts(): Call<List<UserData>>
POJO class
open class UserData {
#SerializedName("userId")
#Expose
open var user_id: Int? = null
#SerializedName("id")
#Expose
open var id: Int? = null
#SerializedName("title")
#Expose
open var title: String? = null
#SerializedName("body")
#Expose
open var body: String? = null
}
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://jsonplaceholder.typicode.com/")
.build()
val service = retrofit.create(APIService::class.java)
service.getPosts().enqueue(object : Callback<List<UserData>> {
override fun onFailure(call: Call<List<UserData>>?, t: Throwable?) {
Log.d("RetrofitTest", t.toString())
}
override fun onResponse(call: Call<List<UserData>>?, response: Response<List<UserData>>?) {
Log.d("RetrofitTest", "onFailure")
}
})
}
}
in APIService interface #GET("posts") insted of #GET("/posts")
It always failed because the reason is the invalid URL. The mistake you have done is '/'. Either you can put the slash('/') in the base URL or at the starting of the Endpoint. In your case, the URL is like "https://jsonplaceholder.typicode.com//posts". Which is invalid that's why your request getting failed. So just remove the '/' from the Endpoint.
Make request interface like this:
interface APIService {
#GET("posts")
fun getPosts(): Call<List<UserData>>
}

Categories

Resources