Retrofit JSONLD ERROR on Android (Java/Kotlin) - android

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

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.

How to merge two lists in repository?

I've got this ApiInterface file with several API calls :
interface ApiInterface {
#GET("wp/v2/mec-events?per_page=50")
fun getData(): Call<List<MyDataItem>>
#GET("wp/v2/mec_location?per_page=50")
fun getPlaces(): Call<List<PlacesItem>>
#GET("wp/v2/mec_category?per_page=50")
fun getCat(): Call<List<CatItem>>
companion object{
var retrofitService: ApiInterface? = null
fun getInstance() : ApiInterface{
val gson = GsonBuilder().setDateFormat("dd-MM-yyyy").create()
val retrofitBuilder = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
return retrofitBuilder
}
}
}
A Repository file :
class MainDataRepository(
private val retrofitService: ApiInterface,
) {
fun getData() = retrofitService.getData()
fun getPlaces() = retrofitService.getPlaces()
fun getCat() = retrofitService.getCat()
}
And a ViewModel :
class MainDataViewModel constructor(private val repository: MainDataRepository) : ViewModel() {
val dataList = MutableLiveData<List<MyDataItem>>()
val errorMessage = MutableLiveData<String>()
fun getData() {
val response = repository.getData()
response.enqueue(object : Callback<List<MyDataItem>> {
override fun onResponse(call: Call<List<MyDataItem>>, response: Response<List<MyDataItem>>) {
dataList.postValue(response.body())
}
override fun onFailure(call: Call<List<MyDataItem>>, t: Throwable) {
errorMessage.postValue(t.message)
}
})
}
val placesList = MutableLiveData<List<PlacesItem>>()
fun getPlaces(){
val response = repository.getPlaces()
response.enqueue(object : Callback<List<PlacesItem>> {
override fun onResponse(call: Call<List<PlacesItem>>, response: Response<List<PlacesItem>>) {
placesList.postValue(response.body())
}
override fun onFailure(call: Call<List<PlacesItem>>, t: Throwable) {
errorMessage.postValue(t.message)
}
})
}
val catList = MutableLiveData<List<CatItem>>()
fun getCat() {
val response = repository.getCat()
response.enqueue(object : Callback<List<CatItem>> {
override fun onResponse(call: Call<List<CatItem>>, response: Response<List<CatItem>>) {
catList.postValue(response.body())
}
override fun onFailure(call: Call<List<CatItem>>, t: Throwable) {
errorMessage.postValue(t.message)
}
})
}
}
My Data lists :
data class MyDataItem(
val id: Int,
val content: Content,
val startdate : String,
val featured_media: Int,
val mec_category: List<String>,
val title: Title,
val cost : String,
val starthours : String,
val endhours : String,
val places : List<String>,
val excerpt : Excerpt,
val link : String
) {}
class CatItem(
val id : Int,
val count : Int,
val name : String,
) {}
class PlacesItem(
val id : Int,
val count : Int,
val name : String
) {}
I can't figure out how to merge these lists in order to have something like this (with the names of categories and places within the main list) :
class NewList(
val id: Int,
val content: Content,
val startdate : String,
val featured_media: Int,
val mec_category: List<String>,
val mec_category_name : String,
val title: Title,
val cost : String,
val starthours : String,
val endhours : String,
val places : List<String>,
val places_name : String,
val excerpt : Excerpt,
val link : String
) {}
the JSON looks like this :
[
{
"id": 1390,
"title": {
"rendered": "MY TITLE"
},
"content": {
"rendered": "MY CONTENT",
"protected": false
},
"excerpt": {
"rendered": "MY EXCERPT",
"protected": false
},
"mec_category": [52],
"startdate": "2021-12-11",
"starthours": "2",
"endhours": "4",
"cost": "0",
"places": ["24"],
},
...]
[{
"id": 24,
"count": 1,
"name": "PLACES NAME",
},
...]
[{
"id": 52,
"count": 3,
"name": "CATEGORY NAME",
},
...]
I've tried to map two lists together but this doesn't work : I can't fetch the value of the 'mec_category' or 'places' because its a list format...
Any clue to help ?

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

Why is my response body null, status 200?

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.

Getting null pointer exception when trying to parse JSON in Retrofit in kotlin with Android studio

I am trying to get a user from Github API. API is working well and receiving the response but getting a problem when trying to parse JSON. kindly guide me. I am Fresh with kotlin and Retrofit. So, please guide me on how to get a proper solution.
Here is my JSON Respons.
{
"login": "photoionized",
"id": 597302,
"node_id": "MDQ6VXNlcjU5NzMwMg==",
"avatar_url": "https://avatars0.githubusercontent.com/u/597302?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/photoionized",
"html_url": "https://github.com/photoionized",
"followers_url": "https://api.github.com/users/photoionized/followers",
"following_url": "https://api.github.com/users/photoionized/following{/other_user}",
"gists_url": "https://api.github.com/users/photoionized/gists{/gist_id}",
"starred_url": "https://api.github.com/users/photoionized/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/photoionized/subscriptions",
"organizations_url": "https://api.github.com/users/photoionized/orgs",
"repos_url": "https://api.github.com/users/photoionized/repos",
"events_url": "https://api.github.com/users/photoionized/events{/privacy}",
"received_events_url": "https://api.github.com/users/photoionized/received_events",
"type": "User",
"site_admin": false,
"name": "Andrew Stucki",
"company": "Aspera, Inc.",
"blog": "",
"location": "Alameda, CA",
"email": null,
"hireable": null,
"bio": null,
"public_repos": 4,
"public_gists": 1,
"followers": 2,
"following": 0,
"created_at": "2011-02-02T18:44:31Z",
"updated_at": "2016-05-12T04:40:54Z"
}
Here is MyApi code
interface MyApi {
#GET("users/{username}")
suspend fun userLogin(
#Path("username") username: String
) : Response<AuthResponse>
companion object{
operator fun invoke() : MyApi {
return Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MyApi :: class.java)
}
}
}
Here is My POKO class
const val CURRENT_USER_ID = 0
#Entity
data class User (
var id: Int? = null,
var name: String? = null,
var email: String? = null,
var login: String? = null,
var avatar_url: String? = null,
var gravatar_id: String? = null,
var url: String? = null,
var html_url: String? = null,
var company: String? = null,
var followers_url: String? = null,
var created_at: String? = null,
var updated_at: String? = null
){
#PrimaryKey(autoGenerate = false)
var uid: Int = CURRENT_USER_ID
}
here is User Repository class code
class UserRepository {
suspend fun userLogin(username: String) : Response<AuthResponse>{
return MyApi().userLogin(username)
}
}
here is Auth Response
data class AuthResponse (
val isSuccessful : Boolean?,
val message: String?,
val user: User?
)
here is AuthViewModel
class AuthViewModel : ViewModel() {
var username: String? = null
var authListener : AuthListener? = null
fun onLoginButtonClick(view: View){
authListener?.onStarted()
if (username.isNullOrEmpty()){
authListener?.onFailure("Invalid email or password")
//
return
}
Coroutines.main{
val response = UserRepository().userLogin(username!!)
if (response.isSuccessful){
authListener?.onSuccess(response.body()?.user!!)
}else{
authListener?.onFailure("Error Code: ${response.code()}")
}
}
}
}
Here is Login Activity
class LoginActivity : AppCompatActivity(), AuthListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding : ActivityLoginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login)
val viewModel = ViewModelProviders.of(this).get(AuthViewModel :: class.java)
binding.viewmodel= viewModel
viewModel.authListener = this
}
override fun onStarted() {
toast("Login Started")
progress_bar.show()
}
override fun onSuccess(user: User) {
progress_bar.hide()
toast("${user.login} is found")
}
override fun onFailure(message: String) {
progress_bar.hide()
toast(message)
}
}
Here is my Logcat error
PID: 15204
kotlin.KotlinNullPointerExceptionat com.isofttechpro.myapplication.ui.auth.AuthViewModel$onLoginButtonClick$1.invokeSuspend(AuthViewModel.kt:23)
KotlinNullPointerException occurs when you force unwrap a null value using !!.
The error probably lies in the following lines,
val response = UserRepository().userLogin(username!!)
if (response.isSuccessful){
authListener?.onSuccess(response.body()?.user!!)
Try to not use !! as much as possible, rather try to use let, apply, run, etc. For example,
//rather than
//authListener?.onSuccess(response.body()?.user!!)
//use
response.body()?.user?.let {
authListener?.onSuccess(it)
}
Another alternative to that is to use the elvis operator and provide default values. For example,
authListener?.onSuccess(response.body()?.user?:"default value")
Firstly, Don't use null to initiate your data class properties.
Secondly, your response is in raw JSON so it doesn't need any parent class to parse.
e.g your data class AuthResponse is not required.
Use the below code to parse it to your data model.
val gson:Gson = Gson()
var user = gson?.fromJson(response.body(), Users::class.java)

Categories

Resources