Why is my response body null, status 200? - android

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.

Related

Api response is never succesfull, It keeps failing

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.

Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 retrofit2

I have problem with my project. It is simple connection with api application. I want to take information like this:
{
"docs": [
{
"_id": "5cf5805fb53e011a64671582",
"name": "The Fellowship Of The Ring"
},
{
"_id": "5cf58077b53e011a64671583",
"name": "The Two Towers"
},
{
"_id": "5cf58080b53e011a64671584",
"name": "The Return Of The King"
}
],
"total": 3,
"limit": 1000,
"offset": 0,
"page": 1,
"pages": 1
}
But I got illegalStateException like in title. Theres is my files:
DTO
data class CharacterDTO(
val docs: List<CharacterData>,
val limit: Int,
val offset: Int,
val page: Int,
val pages: Int,
val total: Int
)
data class CharacterData(
val _id: String,
val birth: String,
val death: String,
val gender: String,
val hair: String,
val height: String,
val name: String,
val race: String,
val realm: String,
val spouse: String,
val wikiUrl: String
)
fun CharacterData.toCharacter(): Character{
return Character(
_id = _id,
birth = birth,
death = death,
gender = gender,
name = name,
race = race
)
}
And it's crash on this file:
USECASE
lass GetCharactersUseCase #Inject constructor(
private val repository: LotrRepository
) {
operator fun invoke(): Flow<List<Character>> = flow {
try {
val characters = repository.getCharacters() // on this line it crash
Log.d(Tag,characters.toString())
}catch (e: HttpException){
Log.d(Tag,e.toString())
}catch (e: IOException){
Log.d(Tag,e.toString())
}
}
}
And this is repository
interface LotrRepository {
suspend fun getBooks(): List<BookDTO>
suspend fun getCharacters(): List<CharacterDTO>
suspend fun getCharacterById(characterId: String): CharacterDTO
}
And last this is my request:
#Provides
#Singleton
fun provideLotrApi(): LotrApi{
val request = Retrofit.Builder()
.client(OkHttpClient.Builder().addInterceptor { chain ->
val request = chain.request().newBuilder().addHeader("Authorization", "Bearer ${Constants.API_KEY}").build()
chain.proceed(request)
}.build())
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(LotrApi::class.java)
return request
}
After my research i forgot to handle with this part
"total": 3,
"limit": 1000,
"offset": 0,
"page": 1,
"pages": 1
But after changes my DTO file it's still don't work
JSON provided above contains only a single CharacterDTO object, but you expected to get a list of such objects. Either change the backend to return a list or replace getCharacters() declaration with:
suspend fun getCharacters(): CharacterDTO

How can I access the nested value in a JSON payload using Gson library with Kotlin

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)
...

Retrofit JSONLD ERROR on Android (Java/Kotlin)

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>>
}

Unable to parse JSON using Retrofit in Android

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.

Categories

Resources