I'm trying to retrieve data and display them in a recycler view. I'm working with retrofit, making a get request.. Everything is fine, except: it won't display data in the recycler because it says: "Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2". I also used the kotlin data class file from JSON plugin to generate data classes from the response. I don't know what to do, I'm stuck..
Here are my classes:
ApiClient.kt
object ApiClient {
private const val BASE_URL: String = "https://dev.fastbeach.it/api/"
private val gson : Gson by lazy {
GsonBuilder().setLenient().create()
}
private val httpClient : OkHttpClient by lazy {
OkHttpClient.Builder().build()
}
private val retrofit : Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
val apiService : ApiService by lazy{
retrofit.create(ApiService::class.java)
}
ApiService.kt
interface ApiService {
@GET("tenant/searchAllWithPagesAndFilters")
fun getUsers(
@Query("reservationEnabled") reservationEnabled:Boolean,
@Query("page") page:Int,
@Query("size") size:Int,
@Query("sort") sort:String,
@Query("direction") direction:String
): Call<MutableList<User>>
UserRepository.kt
object UserRepository {
fun getMutableLiveData(context: Context) : MutableLiveData<ArrayList<User>>{
val mutableLiveData = MutableLiveData<ArrayList<User>>()
context.showProgressBar()
ApiClient.apiService.getUsers(true,0,5,"order","ASC").enqueue(object : Callback<MutableList<User>> {
override fun onFailure(call: Call<MutableList<User>>, t: Throwable) {
hideProgressBar()
Log.e("error", t.localizedMessage)
}
override fun onResponse(
call: Call<MutableList<User>>,
response: Response<MutableList<User>>
) {
hideProgressBar()
val usersResponse = response.body()
usersResponse?.let { mutableLiveData.value = it as ArrayList<User> }
}
})
return mutableLiveData
}
UserViewModel.kt
class UserViewModel(private val context: Context) : ViewModel() {
private var listData = MutableLiveData<ArrayList<User>>()
init{
val userRepository : UserRepository by lazy {
UserRepository
}
if(context.isInternetAvailable()) {
listData = userRepository.getMutableLiveData(context)
}
}
fun getData() : MutableLiveData<ArrayList<User>>{
return listData
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var listUsers: MutableList<User>
private lateinit var adapter: UsersAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler_main.layoutManager = LinearLayoutManager(this@MainActivity)
listUsers = mutableListOf<User>()
adapter = UsersAdapter(this,
listUsers
)
recycler_main.adapter = adapter
val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
userViewModel.getData().observe(this,object:Observer<ArrayList<User>>{
override fun onChanged(t: ArrayList<User>?) {
listUsers.clear()
t?.let { listUsers.addAll(it) }
adapter.notifyDataSetChanged()
}
})
}
API response from POSTMAN
{
"content": [
{
"id": "609d2d79be5f8e42d9c13e41",
"name": "bagno-sirena-posillipo",
"title": "Bagno Sirena Posillipo",
"tenantName": "Bagno Sirena",
"tenantCode": "BSI",
"description": "E' uno dei siti balneari più antichi, inserito in un posto incantevole a ridosso del Palazzo Donn'Anna, in una piccola insenatura riparata e quindi usufruibile in tutti i mesi dell'anno per chi vuole in piena città trascorrere qualche ora di relax respirando aria di mare e conservare l'abbronzatura estiva.\nI gestori si stanno impegnando per migliorare i servizi della location.\nVorremmo condividere con tutti gli amanti del mare e della costa di Posillipo questa meraviglia che abbiamo.\nVeniteci a trovare e avrete modo di constatare questa bellezza.",
"confirmBookingsRequired": false,
"paymentRequired": true,
"fee": 1.0,
"foodFee": 0.5,
"feePercentage": 0.02,
"foodFeePercentage": 0.02,
"percentage": true,
"signInSignOnEnabled": true,
"qrCodeValidation": true,
"tenantImage": {},
"tenantOtherImage": [],
"type": "Beach",
"enabledServices": [],
"master": false,
"comune": "Napoli",
"comuneId": "063049",
"address": {
"address": "Via Posillipo, 357",
"city": "Napoli",
"province": "NA",
"country": "Italia",
"postalNumber": "80123",
"phoneNumber": "081 769 2885",
"web": "https://www.facebook.com/bagnosirenanapoli/",
"email": "development@synclab.it"
},
"reservationEnabled": true,
"foodEnabled": true,
"reservationWithoutMapButWithCart": false,
"criteriaNumPlaces": {},
"supportedServices": [
{
"id": "5f04a23f968f9926b4b76746",
"name": "Lettini",
"description": "Lettini",
"supported": true,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76747",
"name": "Ombrelloni",
"description": "Ombrelloni",
"supported": true,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76748",
"name": "Sdraio",
"description": "Sdraio",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76749",
"name": "Parcheggio",
"description": "Parcheggio",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674a",
"name": "Lettoni",
"description": "Lettoni",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674b",
"name": "Accessibilita",
"description": "Accesso disabili\n",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674c",
"name": "Area_baby",
"description": "Area baby",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674d",
"name": "Ristorante",
"description": "Ristorante",
"supported": true,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674e",
"name": "Bar",
"description": "Bar",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b7674f",
"name": "Alcolici",
"description": "Alcolici",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76750",
"name": "Gazebo",
"description": "Gazebo",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76751",
"name": "Pagoda",
"description": "Pagoda",
"supported": false,
"tenantTypes": []
},
{
"id": "5f04a240968f9926b4b76752",
"name": "Wc",
"description": "Wc",
"supported": true,
"tenantTypes": []
},
{
"id": "60c72a1709e2081fa9b6d422",
"name": "Wi-fi",
"description": "Wi-fi",
"supported": false,
"tenantTypes": []
},
{
"id": "60c72a2509e2081fa9b6d423",
"name": "Accesso_animali",
"description": "Animali",
"supported": false,
"tenantTypes": []
},
{
"id": "60c72a2709e2081fa9b6d424",
"name": "Animazione",
"description": "Animazione",
"supported": false,
"tenantTypes": []
},
{
"id": "60c72a2a09e2081fa9b6d425",
"name": "Cabine",
"description": "Cabine",
"supported": false,
"tenantTypes": []
},
{
"id": "60c72a4109e2081fa9b6d426",
"name": "Piscina",
"description": "Piscina",
"supported": false,
"tenantTypes": []
},
{
"id": "60ec61341dbc2d4ed0d130ff",
"name": "Sedia_regista",
"description": "Sedia regista",
"supported": false,
"searchable": true,
"tenantTypes": []
}
],
"types": [
{
"typeId": "60c73af609e2081fa9b6d42a",
"name": "Sassi",
"description": "Sassi",
"supported": false,
"tenantTypes": []
},
{
"typeId": "60c73af609e2081fa9b6d42b",
"name": "Erba_sintetica",
"description": "Erba sintetica",
"supported": false,
"tenantTypes": []
},
{
"typeId": "60c73af609e2081fa9b6d42c",
"name": "Molo",
"description": "Molo",
"supported": false,
"tenantTypes": []
},
{
"typeId": "60ccb0874107e97fccaac087",
"name": "Spiaggia",
"description": "Spiaggia",
"supported": true,
"tenantTypes": []
},
{
"typeId": "60ccb0874107e97fccaac088",
"name": "Erba_naturale",
"description": "Erba naturale",
"supported": false,
"tenantTypes": []
},
{
"typeId": "60ccb0874107e97fccaac089",
"name": "Ciottoli",
"description": "Ciottoli",
"supported": false,
"tenantTypes": []
}
],
"targets": [
{
"id": "60c73af609e2081fa9b6d42d",
"name": "Famiglie",
"description": "Famiglie",
"supported": true,
"tenantTypes": []
},
{
"id": "60c73af609e2081fa9b6d42e",
"name": "Coppie",
"description": "Coppie",
"supported": true,
"tenantTypes": []
},
{
"id": "60c73af609e2081fa9b6d42f",
"name": "Ragazzi",
"description": "Ragazzi",
"supported": false,
"tenantTypes": []
}
],
"visible": true,
"maxNumBookingGuests": 0,
"numBookablePlaces": 0,
"maxPlacesPerBooking": 0,
"withoutLayout": false,
"lastMinute": true,
"upfront": false,
"latitude": 40.8197859,
"longitude": 14.2113269,
"order": 1,
"bookingUserDetailActive": false
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"pageNumber": 0,
"pageSize": 1,
"offset": 0,
"paged": true,
"unpaged": false
},
"last": true,
"totalElements": 1,
"totalPages": 1,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"first": true,
"number": 0,
"numberOfElements": 1,
"size": 1,
"empty": false
Data classes generated through the plugin. (I'll just paste this one)
data class User(
@SerializedName("content")
val content: Content? = null,
@SerializedName("empty")
val empty: Boolean? = null,
@SerializedName("first")
val first: Boolean? = null,
@SerializedName("last")
val last: Boolean? = null,
@SerializedName("number")
val number: Int? = null,
@SerializedName("numberOfElements")
val numberOfElements: Int? = null,
@SerializedName("size")
val size: Int? = null,
@SerializedName("totalElements")
val totalElements: Int? = null,
@SerializedName("totalPages")
val totalPages: Int? = null
) : Serializable