How to Parse Json in Kotlin Using Retrofit? - android

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.

Related

How to parse response with retrofit2 Kotlin

I'm stuck with parsing the response. In Swift I can make a codable to help parsing the json response. I'm new to Kotlin and I'm working on someone else existing project. I made a data class for string and boolean but I don't know the syntax to parse it. Please help and thank you.
The responseBody json
{
"bearerToken": "########",
"staySignIn": false
}
//Interface
interface PostInterface {
class User(
val email: String,
val password: String
)
#POST("signIn")
fun signIn(#Body user: User): Call<ResponseBody>
//Network handler
fun signIn(email: String, password: String): MutableLiveData<Resource> {
val status: MutableLiveData<Resource> = MutableLiveData()
status.value = Resource.loading(null)
val retrofit = ServiceBuilder.buildService(PostInterface::class.java)
retrofit.signIn(PostInterface.User(email, password)).enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
errorMessage(status)
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.code() == 200) {
try {
status.value = //how to parse using the model??
} catch (ex: Exception) {
parseError(400, response.body().toString(), status)
}
} else {
//do something...
}
}
})
return status
}
//Model
data class SignInModel(
#field:SerializedName("bearerToken")
val bearerToken: String? = null,
#field:SerializedName("staySignIn")
val staySignIn: Boolean? = null
)
//Storing value class
class RrefManager constructor(var applicationContext: Context) {
private fun getSharedPrefEditor(): sharedPrefEditor.Editor {
return applicationContext.getSharedPrefEditor(prefStorageName, Context.MODE_PRIVATE).edit()
}
public fun setBearerToken(token: String) {
getSharedPrefEditor().putString("bearerToken", token).apply()
}
public fun setStaySignIn(enabled: Boolean) {
getSharedPrefEditor().putBoolean("staySignIn", enabled).apply()
}
}
//SignIn Button
viewModel.signIn().observe(viewLifecycleOwner, androidx.lifecycle.Observer { v ->
if (v.status == Resource.Status.SUCCESS) {
val model = v.data as SignInModel
pref.setToken(model.token as String) //storing value
pref.setTwoFactorEnabled(model.twoFactorEnabled as Boolean) //storing value
} else if (v.status == Resource.Status.ERROR) {
//do something...
}
})
I think your best option to achieve something like the codable in swift is to use Gson library for parsing api responses.
When you create the retrofit instance you pass the gson converter to the builder like:
val retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
After you have done that you can make the api return the response you have as the data class, like:
//Interface
interface PostInterface {
#POST("signIn")
fun signIn(#Body user: User): Call<SignInModel>
}
To read the answer from the callback on your class, the response inside the network call is already parsed into your model in the callback. All the retrofit callback should be changed to receive Callback and then you can access directly like status.value = response.body()
For more info you can consult the retrofit library page where it gives all the details and explanations on how to use it correctly.
https://square.github.io/retrofit/

how to show list from POST response using retrofit?

I want to show response POST from retrofit. I don't really know how because my response are array but with POST.
this is how my response looks like
{
"message":[
"00",
"Get Success"
],
"result":{
"listProgram":[
{
"banner":"",
"area":"",
"domainlembaga":"",
"domainprogram":"",
"donate":0
{
.
.
.
]
service
interface Service {
#POST("program/list")
fun getProgram(#Body body: Pair<String, Int>, pair: Pair<String, Int>): Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>
data
data class BaseBersedekahResponse<T>(
#SerializedName("message") val message: String?,
#SerializedName("result") val result: T?
)
data class ListProgramBersedekahResponse(
#SerializedName("listProgram") val listProgram: List<ProgramBersedekahResponse>?
)
data class ProgramBersedekahResponse(
#SerializedName("banner") val banner: String?,
#SerializedName("domainlembaga") val domainlembaga: String?,
#SerializedName("domainprogram") val domainprogram: String?,
#SerializedName("donate") val donatur: Int?
)
activity
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(APIUrl.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService: Service = retrofit.create(Service::class.java)
val call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>> = apiService.getProgram(
"limit" to 1,
"group" to 4)
call.enqueue(object : Callback<BaseBersedekahResponse<ListProgramBersedekahResponse>> {
override fun onResponse(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>, response: Response<BaseBersedekahResponse<ListProgramBersedekahResponse>>) {
val getProgram: List<ProgramBersedekahResponse?>? = response.body().result.listProgram
Toast.makeText(applicationContext, "success", Toast.LENGTH_SHORT).show()
}
override fun onFailure(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
I actually planning it to show in RecyclerView, but I still don't know how to show it the response from POST. please help.
Your signatures should match each other. You should make your signatures same as the signature in your interface.
For your request body:
data class BersedekahRequest(
#SerializedName("limint") val limint: Int,
#SerializedName("group") val group: Int
)
then your interface:
#POST("program/list")
fun getProgram(#Body body: BersedekahRequest): Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>
your rest service call:
val requestBody = BersedekahRequest(10,20)
val call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>> = apiService.getProgram(requestBody)
call.enqueue(object : Callback<BaseBersedekahResponse<ListProgramBersedekahResponse>> {
override fun onResponse(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>, response: Response<BaseBersedekahResponse<ListProgramBersedekahResponse>>) {
val GetApps2: List<ProgramBersedekahResponse?>? = response.body().result.listProgram
Toast.makeText(applicationContext, "success", Toast.LENGTH_SHORT).show()
}
override fun onFailure(call: Call<ListProgramBersedekahResponse>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
And I think one more problem you have.
fun getProgram(#Body body: ProgramBersedekahResponse?) this #Body is the body you will send via POST request, not your response body. If you do not send anything inside post body, make it empty, or give the appropriate request body since it seems like it is your response object right now.

Android get data from dynamic url from APi

I found some Api about Pokemon(https://pokeapi.co). And I try get data from this Api and it's work.
The data look like this
But I try to use name of data from Api to get some image with this path "api/v2/pokemon/{name}"
The problem is how can get name out of onResponse or do the other way to get name and image of Pokemon
MainActivity
val retrofit = Retrofit.Builder()
.baseUrl("https://pokeapi.co/")
.addConverterFactory(GsonConverterFactory.create())
.client(HTTPLogger.getLogger())
.build()
val jsonPlaceholderApi = retrofit.create(pokemonService::class.java)
val myCall: Call<PokemonInGen> = jsonPlaceholderApi.getGen(1)
myCall.enqueue(object : Callback<PokemonInGen> {
override fun onResponse(
call: Call<PokemonInGen>,
response: Response<PokemonInGen>
) {
val DataResponse: PokemonInGen = response.body()!!
Timber.i("on do Respon %s", DataResponse)
}
override fun onFailure(call: Call<PokemonInGen>, t: Throwable) {
Timber.i("on do ERROR")
}
})
My Service
interface pokemonService {
#GET("api/v2/generation/{id}")
fun getGen(
#Path("id") id: Int,
): Call<PokemonInGen>
#GET("api/v2/pokemon/{name}")
fun getArtwork(
#Path("name") name: String,
): Call<PokemonArtwork>
}
My Model Data class
data class PokemonInGen(
val pokemon_species: List<PokemonList>)
data class PokemonList(
val name: String,
val url: String,
)
To fetch Pokemon image you should create additional function in your MainActivity class
fun fetchPokemonArtwork(name: String) {
jsonPlaceholderApi.getArtwork(name).enqueue(object : Callback<PokemonArtwork> {
override fun onResponse(
call: Call<PokemonArtwork>,
response: Response<PokemonArtwork>
) {
// An artwork is successful fetched
val artwork = response.body()!!
}
override fun onFailure(call: Call<PokemonArtwork>, t: Throwable) {
// Handle a failure
}
})
}
You should call this function right after you fetched a Pokemon in generation
val myCall: Call<PokemonInGen> = jsonPlaceholderApi.getGen(1)
myCall.enqueue(object : Callback<PokemonInGen> {
override fun onResponse(
call: Call<PokemonInGen>,
response: Response<PokemonInGen>
) {
val DataResponse: PokemonInGen = response.body()!!
Timber.i("on do Respon %s", DataResponse)
// Extract name
val name = DataResponse.pokemon_species.first().name
// Fetch an artwork
fetchPokemonArtwork(name)
}
override fun onFailure(call: Call<PokemonInGen>, t: Throwable) {
Timber.i("on do ERROR")
}
})
P.S. I proceeded from the assumption that you've implemented PokemonArtwork class. Please let me know if you are facing difficulties in comments below.
P.S.S. It's not recommended to make network calls in Activity or Fragment classes. This guide to app architecture should help you to select correct app structure in your future releases.
You can using #Url to support dynamic link in retrofit. Example below:
interface pokemonService {
#GET
Call<PokemonResponse> getListPokemon(#Url String url);
}
And paste your url to browser to see data format.

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.

Empty response body on retrofit call

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)

Categories

Resources