How to merge two lists in repository? - android

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 ?

Related

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

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 148 path $.main

i have problem and really don't know how to fix this. I try to find similar posts several days, but didn't find.
I use retrofit for parsing api and put it in room database and use rxjava3
because it will be asynchronously
That my JSON
{
"coord": {
"lon": -0.1257,
"lat": 51.5085
},
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04d"
}
],
"base": "stations",
"main": {
"temp": 22.78,
"feels_like": 22.81,
"temp_min": 21.23,
"temp_max": 23.92,
"pressure": 1020,
"humidity": 65
},
"visibility": 10000,
"wind": {
"speed": 0.45,
"deg": 264,
"gust": 2.68
},
"clouds": {
"all": 75
},
"dt": 1623415339,
"sys": {
"type": 2,
"id": 2019646,
"country": "GB",
"sunrise": 1623383015,
"sunset": 1623442617
},
"timezone": 3600,
"id": 2643743,
"name": "London",
"cod": 200
}
Api service
interface OpenWeatherApiService {
#GET("weather")
fun getCurrentWeather(
#Query("q") location:String,
#Query("appid") key:String,
#Query("units") units:String,
#Query("lang") language:String = "en"
):Observable<CurrentWeatherResponse>}
That my app module
#Module
#InstallIn(ActivityComponent::class)
object AppModule {
#Provides
fun provideOkHttpClient() =if(BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
OkHttpClient
.Builder()
.addInterceptor(interceptor)
.build()
}else{
OkHttpClient
.Builder()
.build()
}
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl("https:/api.openweathermap.org/data/2.5/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
#Provides
fun provideGson(): Gson = GsonBuilder().create()
#Provides
fun provideOpenWeatherApiService(retrofit: Retrofit):OpenWeatherApiService = retrofit.create(OpenWeatherApiService::class.java)
#Provides
fun provideOpenWeatherApiHelper(openWeatherApiHelper: OpenWeatherApiHelperImpl):OpenWeatherApiHelper = openWeatherApiHelper
#Provides
fun provideForecastDatabase(#ApplicationContext appContext: Context) = ForecastDatabase.getDatabase(appContext)
#Provides
fun provideCurrentWeatherDao(db: ForecastDatabase) = db.currentWeatherDao()
}
My response
const val CURRENT_WEATHER_ID = 0
#Entity(tableName = "current_weather")
data class CurrentWeatherResponse(
val main: List<Main> ,
val name: String? = "",
val visibility: Int? = 0,
val weather: List<Weather> ,
val wind:List<Wind>
){
#PrimaryKey(autoGenerate = false)
var id_current_weather:Int = CURRENT_WEATHER_ID
}
My type converter for Main, i put it in on a database
class MainConverter {
val gson = Gson()
#TypeConverter
fun listMainToString(mainList: List<Main?>?):String?{
return gson.toJson(mainList)
}
#TypeConverter
fun stringToListMain(dataMain:String?):List<Main?>?{
if (dataMain == null){
return Collections.emptyList()
}
val listType: Type = object :
TypeToken<List<Main>?>() {}.type
return gson.fromJson<List<Main?>?>(dataMain,listType)
}
}
The data class you are generating for your JSON response is not correct. Many of the things are objects, but you have assigned it as a List item. Here is the correct data class response based on your JSON. So the JSON response is not being parsed properly.
data class CurrentWeatherResponse(
val base: String,
val clouds: Clouds,
val cod: Int,
val coord: Coord,
val dt: Int,
val id: Int,
val main: Main,
val name: String,
val sys: Sys,
val timezone: Int,
val visibility: Int,
val weather: List<Weather>,
val wind: Wind
)
data class Clouds(
val all: Int
)
data class Coord(
val lat: Double,
val lon: Double
)
data class Main(
val feels_like: Double,
val humidity: Int,
val pressure: Int,
val temp: Double,
val temp_max: Double,
val temp_min: Double
)
data class Sys(
val country: String,
val id: Int,
val sunrise: Int,
val sunset: Int,
val type: Int
)
data class Weather(
val description: String,
val icon: String,
val id: Int,
val main: String
)
data class Wind(
val deg: Int,
val gust: Double,
val speed: Double
)
I suggest you try to use this and try again. Here is also a plugin that automatically generates data classes based of JSON. It might help you more.
https://plugins.jetbrains.com/plugin/10054-generate-kotlin-data-classes-from-json

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.

Could not map the json data to pojo class using retrofit

As I viewed the success API response, it seems it is sending the data but the data could not load to its corresponding Pojo class, where the size of ArrayList seems null. The POJO class structure seems fine from my side but could not figure out what the problem is. Here I've provided my log screenshot which returned the API data and the Log I kept to view the ArrayList size:
API Response:
{
"achievementList": [
{
"id": "somerandomuuid2",
"name": "Foodie",
"url": "https://toppng.com/uploads/preview/achievement-icon-icon-11553495882s4jdqrtwe2.png",
"rewardPoint": 50,
"description": "Order 500 Food items.",
"earnPoint": 500,
"userPoint": 450,
"achieved": false
},
{
"id": "somerandomuuid3",
"name": "Explorer",
"url": "https://toppng.com/uploads/preview/achievement-icon-icon-11553495882s4jdqrtwe2.png",
"rewardPoint": 50,
"description": "Book more than 100 tickets.",
"earnPoint": 100,
"userPoint": 0,
"achieved": false
}
],
"totalRewardPoints": 0
}
Achievements (POJO Class)
data class Achievements(
#Json(name = "achievementList")
var achievementsList: ArrayList<AchievementsList>
)
data class AchievementsList(
#Json(name = "id")
var id: String?,
#Json(name = "name")
var name: String?,
#Json(name = "url")
var url: String?,
#Json(name = "rewardPoint")
var rewardPoint: Int?,
#Json(name = "description")
var description: String?,
#Json(name = "earnPoint")
var earnPoint: Int?,
#Json(name = "userPoint")
var userPoint: Int?,
#Json(name = "achieved")
var achieved: Boolean?
)
APIService
fun getUserAchievements(
context: AppCompatActivity,
userId: String,
listener: OnAchievementsListener
) {
APIClient.normalRequest.getUserAchievements(userId)
.enqueue(object : Callback<Achievements> {
override fun onFailure(call: Call<Achievements>, t: Throwable) {
listener.onFailure(ResponseCodes.badRequest, t.localizedMessage!!)
}
override fun onResponse(
call: Call<Achievements>,
response: Response<Achievements>
) {
when (response.code()) {
ResponseCodes.success -> {
Log.i("ProfileActivity: ", "Response: ${response.body()}")
response.body()?.let { listener.onSuccess(it) }
}
else -> {
when (response.code()) {
listener.onFailure(response.code(), "Something went wrong")
}
}
}
}
})
}
Activity
private fun getAchievementsList() {
appPreferences?.getString(AppPreferences.UUID)?.let {
getUserAchievements(this, it, object : OnAchievementsListener {
override fun onFailure(code: Int, description: String) {
Utils.showToast(this#ProfileActivity, "$code, $description")
}
override fun onSuccess(achievements: Achievements?) {
Log.i(TAG, "Size: ${achievements?.achievementsList?.size}")
}
})
}
}
Try this:
#SerializedName("achievementList")
instead of
#Json(name = "achievementList")
in POJO class all field

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