i want my api response to load into a livedata from my viewmodel of my fragment. But my api response is never succesfull. Ive checked the URL and parameters, headers etc. And it's all correct.
client:
object KinetixClient
{
private const val BASE_URL = "https://exercisedb.p.rapidapi.com/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
val apiservice by lazy{
retrofit.create(KinetixApiService::class.java)
}
}
service:
#Headers(
"X-RapidAPI-Key: ...",
"X-RapidAPI-Host: ..."
)
#GET("exercises")
fun getexerciceList(): Call<List<ExerciseResult>>
response:
data class ExerciseResult (
#Json(name = "id")
val id: Int,
#Json(name = "name")
val name: String,
#Json(name = "target")
val target: String,
#Json(name = "equipment")
val equipment: String,
#Json(name = "bodypart")
val bodypart: String,
#Json(name = "gifurl")
val gifUrl: String
)
in my Fragments viewmodel: (this method should shuffle the response and take only 10 elements)
fun getRandomexercices(){
val client = KinetixClient.apiservice.getexerciceList()
if(client == null){
Log.d("Fiel","Client is empty")
}
else{
Log.d("Fiel", client.toString())
}
client.enqueue(object : Callback<List<ExerciseResult>> {
override fun onResponse(
call: Call<List<ExerciseResult>>,
response: Response<List<ExerciseResult>>
) {
if(response.isSuccessful){
_RandomExer.postValue(response.body()?.shuffled()?.take(10))
Log.d("Response","RandomExercises is filled")
}
else{
Log.d("Response", "Response code: ${response.code()}")
}
}
override fun onFailure(call: Call<List<ExerciseResult>>, t: Throwable) {
_RandomExer.postValue(null)
Log.d("Response","Failed")
}
})
if(_RandomExer.value == null){
Log.d("Fiel","EMPTY")
}
else{
Log.d("Fiel","FILLED")
}
}
I have used some logging to see where i get and i keep getting "Response" - "Failed". It's never succesfull.
Json response:
[
{
"bodyPart": "waist",
"equipment": "body weight",
"gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/0001.gif",
"id": "0001",
"name": "3/4 sit-up",
"target": "abs"
},
{
"bodyPart": "waist",
"equipment": "body weight",
"gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/0002.gif",
"id": "0002",
"name": "45° side bend",
"target": "abs"
},
{
"bodyPart": "waist",
"equipment": "body weight",
"gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/0003.gif",
"id": "0003",
"name": "air bike",
"target": "abs"
},
{
"bodyPart": "upper legs",
"equipment": "body weight",
"gifUrl": "http://d205bpvrqc9yn1.cloudfront.net/1512.gif",
"id": "1512",
"name": "all fours squad stretch",
"target": "quads"
},
I have no idea what to change to my code. Please tell me what's wrong.
Related
I need to parse below JSON payload:
{
"status": "success",
"data": {
"stats": {
"total": 11812,
"offset": 0,
"limit": 50,
"order": "desc",
"base": "USD",
"totalMarkets": 77573,
"totalExchanges": 372,
"totalMarketCap": 1692792022714.2244,
"total24hVolume": 78345365115.11235
},
"base": {
"symbol": "USD",
"sign": "$"
},
"coins": [
{
"id": 1,
"uuid": "Qwsogvtv82FCd",
"slug": "bitcoin-btc",
"symbol": "BTC",
"name": "Bitcoin",
...
}
I have a problem with "coins" value:
fun fetchJson() {
val url = "https://api.coinranking.com/v1/public/coins"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
println("Failed")
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
val body = response?.body?.string()
println(body)
val gson = GsonBuilder().create()
val coins = gson.fromJson(body, coinrank::class.java)
println("THESE ARE THE COINS : " +coins)
}
})
}
}
Data model:
class coinrank(val status: String?, val data: Array<dataR>?)
class dataR (val coins: List<justCoin>?)
class justCoin (
val id: Int?,
val name: String?,
val description: String?,
val slug: String?,
val symbol: String?,
val iconUrl: String?
)
There's an error:
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but
was BEGIN_OBJECT at line 1 column 29 path $.data
on the line: val coins = gson.fromJson(body, coinrank::class.java)
I tried change the val data: Array<dataR>? to val data: JSONObject" but I still can't find a way to access the values, can somebody help me?
Your data model does not fit to the JSON payload. Try below:
data class CoinsResponse(
val status: String,
val data: CoinsData
)
data class CoinsData(
val coins: List<Coin>
)
data class Coin(
val id: Int,
val symbol: String,
val name: String
)
Example usage:
val gson = GsonBuilder().create()
val response = gson.fromJson(body, CoinsResponse::class.java)
response.data.coins.forEach(System.out::println)
Above code should print:
Coin(id=1, symbol=BTC, name=Bitcoin)
Coin(id=2, symbol=ETH, name=Ethereum)
Coin(id=8, symbol=USDT, name=Tether USD)
Coin(id=14, symbol=BNB, name=Binance Coin)
Coin(id=9, symbol=ADA, name=Cardano)
Coin(id=3, symbol=XRP, name=XRP)
...
I am trying to get response body from this url:http:"//192.168.0.220:8000/records/?account_id=2"
In android studio i get status 200 but body is always null. Can anybody please tell me what I am doing wrong?
Response in postman looks like this:
{
"result": [
{
"id": 1,
"account_id": 2,
"title": "ez",
"datetime": "2021-03-21T00:00:00",
"description": "ez2",
"image": null,
"recording": null
},
{
"id": 2,
"account_id": 2,
"title": "ez",
"datetime": "2021-03-21T00:00:00",
"description": "ez2",
"image": null,
"recording": null
},
....
Response in android studio:
I/System.out: Response{protocol=http/1.1, code=200, message=OK, url=http://192.168.0.220:8000/records/?account_id=2}
Item(id=null, account_id=null, title=null, datetime=null, image=null, recording=null)
Interface:
interface Gett {
#GET("?account_id=2")
fun getRecord(): Call<Record.Item>
}
Class:
class Record {
data class Item(
val id: String,
val account_id: String,
val title: String,
val datetime: String,
val image: String,
val recording: String
)
}
MainActivity:
val retrofit = Retrofit.Builder()
.baseUrl("http://192.168.0.220:8000/records/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(Gett::class.java)
val call = service.getRecord()
call.enqueue(object : retrofit2.Callback<Record.Item> {
override fun onResponse(call: Call<Record.Item>, response: Response<Record.Item>) {
if (response.code() == 200) {
println(response)
println(response.body()!!)
}
}
override fun onFailure(call: Call<Record.Item>, t: Throwable) {
println("fail")
}
})
The issue might be this
interface Gett {
#GET("?account_id=2")
fun getRecord(): Call<Record.Item> <----
}
So, change your model
data class Record (
val result: List<Item>
)
data class Item(
val id: String,
val account_id: String,
val title: String,
val datetime: String,
val image: String,
val recording: String
)
As I can see your JSON has an array of your Item so change it to.
interface Gett {
#GET("?account_id=2")
fun getRecord(): Call<Record>
}
Thanks, #Kunn for pointing out JSONObject.
i am trying to retrieve a JSON response that looks like this :
{
"#context": "/api/contexts/Advert",
"#id": "/api/adverts",
"#type": "hydra:Collection",
"hydra:member": [
{
"#id": "/api/adverts/6",
"#type": "Advert",
"id": 6,
"title": "PS5 Neuve !",
"content": "Je vends la toute nouvelle Playstation 5, le prix est non négociable.",
"author": "Jonathan Kaekr",
"email": "JonhatanK#hotmail.fr",
"category": "/api/categories/2",
"price": 995,
"state": "draft",
"createdAt": "2020-11-25T09:29:13+00:00",
"publishedAt": "2020-11-25T09:33:59+00:00",
"image": null
},
But when i try to retrieve it with Retrofit it gives me this error : Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
This the files that i use :
Interface.kt :
package com.example.lebonangle.data
import retrofit2.Call
import retrofit2.http.GET
interface LeBonAngleApi {
#GET("http://192.168.1.10:8000/api/adverts?page=1")
fun getAdverts(): Call<List<Adverts>>
}
Class.kt :
package com.example.lebonangle.data
class Adverts {
var title: String? = null
var content: String? = null
var author: String? = null
var email: String? = null
var price: Int? = null
}
and the function that i use in my mainActivity :
val retrofit = Retrofit.Builder()
.baseUrl(advertUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(LeBonAngleApi::class.java)
val adverts = service.getAdverts()
adverts.enqueue(object: Callback<List<Adverts>> {
override fun onResponse(call: Call<List<Adverts>>, response: Response<List<Adverts>>) {
val allTanStop = response.body()
allTanStop?.let {
for( advert in it) {
Log.d("ADD","Annonce ${advert.title}")
}
}
}
override fun onFailure(call: Call<List<Adverts>>, t: Throwable) {
Log.e("ADD", "Error : $t")
}
})
according Call<List> api , We expect the response to be an array , But json object not begin with array
correct api call:
package com.example.lebonangle.data
import retrofit2.Call
import retrofit2.http.GET
interface LeBonAngleApi {
#GET("http://192.168.1.10:8000/api/adverts?page=1")
fun getAdverts(): Call<Advert>
}
correct model:
package com.example.lebonangle.data
data class Advert(
#SerializedName("#context")
var context: String?,
#SerializedName("hydra:member")
var hydraMember: List<Any>?,
#SerializedName("#id")
var id: String?,
#SerializedName("#type")
var type: String?
)
I have same pb.
I create class for that :
data class HydraMember<T> (
#SerializedName("hydra:member")
var hydraMember: List<T>? = null
)
and api calls :
interface myApiCalls{
#GET("api/first_model")
fun getFirstModels(): Call<HydraMember<FirstModel>>
#GET("api/second_model")
fun getSecondModels(): Call<HydraMember<SecondModel>>
}
I am successfully able to hit the API and get the json result. I can see the success result in the logs by printing Retrofit response body. and also using Stetho as the network interceptor.
However, I am not able to understand why is the api response still "null" in the onResponse() method in the repository. I believe, I am not passing the correct model maybe for the JSON to be parsed properly ? Can anybody help me to find out what's the issue here?
Following is the json:
{
"photos": {
"page": 1,
"pages": 2864,
"perpage": 100,
"total": "286373",
"photo": [
{
"id": "49570734898",
"owner": "165034061#N07",
"secret": "f3cb2c2590",
"server": "65535",
"farm": 66,
"title": "Hello",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
],
"photo": [
{
"id": "12344",
"owner": "23444#N07",
"secret": "f233edd",
"server": "65535",
"farm": 66,
"title": "Hey",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
}
]
},
"stat": "ok"
}
My Pojo Class :
data class Photos(
#SerializedName("page")
val page: Int,
#SerializedName("pages")
val pages: Int,
#SerializedName("perpage")
val perpage: Int,
#SerializedName("photo")
val photos: List<Photo>,
#SerializedName("total")
val total: String
)
data class Photo(
#SerializedName("farm")
val farm: Int,
#SerializedName("id")
val id: String,
#SerializedName("isfamily")
val isFamily: Int,
#SerializedName("isfriend")
val isFriend: Int,
#SerializedName("ispublic")
val isPublic: Int,
#SerializedName("owner")
val owner: String,
#SerializedName("secret")
val secret: String,
#SerializedName("server")
val server: String,
#SerializedName("title")
val title: String
)
RetrofitClient:
object ApiClient {
private val API_BASE_URL = "https://api.flickr.com/"
private var servicesApiInterface: ServicesApiInterface? = null
fun build(): ServicesApiInterface? {
val builder: Retrofit.Builder = Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.addInterceptor(interceptor()).addNetworkInterceptor(StethoInterceptor())
val retrofit: Retrofit = builder
.client(httpClient.build()).build()
servicesApiInterface = retrofit.create(
ServicesApiInterface::class.java
)
return servicesApiInterface as ServicesApiInterface
}
private fun interceptor(): HttpLoggingInterceptor {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return httpLoggingInterceptor
}
interface ServicesApiInterface {
#GET("/services/rest/?method=flickr.photos.search")
fun getImageResults(
#Query("api_key") apiKey: String,
#Query("text") text: String,
#Query("format") format: String,
#Query("nojsoncallback") noJsonCallback: Boolean
): Call<PhotoResponse>
}
}
OperationCallback:
interface OperationCallback<T> {
fun onSuccess(data:List<T>?)
fun onError(error:String?)
}
PhotoDataSource:
interface PhotoDataSource {
fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String)
fun cancel()
}
PhotoRepository:
class PhotoRepository : PhotoDataSource {
private var call: Call<PhotoResponse>? = null
private val API_KEY = "eff9XXXXXXXXXXXXX"
val FORMAT = "json"
companion object {
val TAG = PhotoRepository::class.java.simpleName
}
override fun retrievePhotos(callback: OperationCallback<Photo>, searchText: String) {
call = ApiClient.build()
?.getImageResults(
apiKey = API_KEY,
text = searchText,
format = FORMAT,
noJsonCallback = true
)
call?.enqueue(object : Callback<PhotoResponse> {
override fun onFailure(call: Call<PhotoResponse>, t: Throwable) {
callback.onError(t.message)
}
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
Log.d(TAG, "got api response total pics are :${it.data?.size}")
if (response.isSuccessful && (it.isSuccess())) {
callback.onSuccess(it.data)
} else {
callback.onError(it.msg)
}
}
}
})
}
override fun cancel() {
call?.let {
it.cancel()
}
}
}
PhotoResponse:
data class PhotoResponse(val status: Int?, val msg: String?, val data: List<Photo>?) {
fun isSuccess(): Boolean = (status == 200)
}
Try to change your PhotoResponse to match with your json response.
data class PhotoResponse(
#SerializedName("stat")
val status: String?,
#SerializedName("photos")
val photos: Photos?
) {
fun isSuccess(): Boolean = status.equals("ok", true)
}
And then inside onResponse, You can get List<Photo> like below:
override fun onResponse(
call: Call<PhotoResponse>,
response: Response<PhotoResponse>
) {
response?.body()?.let {
//This should be your list of photos
it.photos.photos
}
}
The issue is with your data class. You need one extra data class here.
So if you look at your JSON response closely, then you will understand whats going wrong.
Your photos data class should not be the first class. Instead it should be inside one more class lets say PhotoApiResponse.
Your first class will contain both photos and stat.
And then rest can be the same.
How can I get JSONObject which has Array in it in volley?
Logic
Code
Note: I'm aware that my api function below works if my result is JSON
Array, but I'm not sure how to modify it in order to get JSON Object
(as my result is)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_posts)
callAPIDemo()
}
// api code
private fun callAPIDemo() {
val mySlugValue: String = intent.getStringExtra("my_slug")
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "https://example.com/api/categories/$mySlugValue"
// Request a string response from the provided URL.
val stringRequest = StringRequest(
Request.Method.GET, url,
Response.Listener<String> { response ->
val jsonArray = JSONArray(response)
val list: ArrayList<Post> = ArrayList()
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
list.add(parseData(jsonObject))
}
// here you will have the complete list of data in your "list" variable
posts_list.layoutManager = LinearLayoutManager(this)
Log.d("my list", list.toString())
posts_list.adapter = MyPostsRecyclerViewAdapter(list)
},
Response.ErrorListener { error ->
//displaying the error in toast if occurrs
Toast.makeText(applicationContext, error.message, Toast.LENGTH_SHORT)
.show()
})
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
// parsing data
private fun parseData(jsonObject: JSONObject): Post {
var listingObject = Post(
jsonObject.getString("name"),
jsonObject.getString("slug"),
jsonObject.getString("image")
)
return listingObject
}
Any idea?
Update
As requested here is how my returned code looks like:
{
"id": 10,
"name": "...",
"slug": "...",
"icon": "...",
"body": "...",
"image": "...",
"posts": [
{
"id": 2,
"user": "...",
"name": "...",
"slug": "...",
"image": "...",
"body": "...",
"icon": null,
"quote": null,
"video": null,
"created_at": "2019-11-23 06:05:56",
"updated_at": "2019-11-23 06:53:26"
},
// other posts
],
"created_at": "2019-11-23 05:35:31",
"updated_at": "2019-11-26 11:25:17"
}
You can parse your json data like this
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_posts)
callAPIDemo()
}
private fun callAPIDemo() {
val mySlugValue: String = intent.getStringExtra("my_slug")
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "https://example.com/api/categories/$mySlugValue"
// Request a string response from the provided URL.
val stringRequest = StringRequest(
Request.Method.GET, url,
Response.Listener<String> { response ->
val list: ArrayList<Post> = ArrayList()
getPosts(response,list)
// here you will have the complete list of data in your "list" variable
posts_list.layoutManager = LinearLayoutManager(this)
Log.d("my list", list.toString())
posts_list.adapter = MyPostsRecyclerViewAdapter(list)
},
Response.ErrorListener { error ->
//displaying the error in toast if occurrs
Toast.makeText(applicationContext, error.message, Toast.LENGTH_SHORT)
.show()
})
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
fun getPosts(response: String,list:ArrayList<Post>) {
var jsonObject = JSONObject(response)
val jsonArray = jsonObject.getJSONArray("posts")
for (i in 0 until jsonArray.length()) {
val jsonObject1 = jsonArray.getJSONObject(i)
var listingObject = Post(
jsonObject1.getInt("id"),
jsonObject1.getString("user"),
jsonObject1.getString("slug"),
jsonObject1.getString("image"),
jsonObject1.getString("body"),
jsonObject1.getString("icon"),
jsonObject1.getString("quote"),
jsonObject1.getString("video"),
jsonObject1.getString("created_at"),
jsonObject1.getString("updated_at")
)
list.add(listingObject)
}
}
And your data class will look like this
data class Post ( val id: Int, val user: String?, val slug: String?,
val image: String?, val body: String?, val icon: String?,
val quote: String?, val video: String?, val created_at: String?,
val updated_at: String?
)