0

I am new in Android development, and I am trying to get data from server. the general JSON response structure will be like this

{
    "success": "1",
    "data": [
        {
            "customers_id": 4,
            "customers_gender": "0",
            "customers_firstname": "TES IOS",
            "customers_lastname": "TES IOS",
            "customers_dob": "2018-12-27",
            "email": "TES002@email.com",
            "user_name": "TES002",
            "customers_default_address_id": 0,
            "customers_telephone
         },
    "message": "Successfully get user data from server"
}

the "success" and "message" field will be the same (will always be string). but the "data" can be different for other request call. It can send user data, store data or product data, or even Array/List of Products.

so I want to make general reusable class to catch that JSON response. the class will be like this, I set the "data" to be Any, and then later it will be casted back to User object:

class ServerData(successStatus: Int, data: Any, message: String) {

    val isSuccessfull : Boolean
    val data : Any
    val message : String


    init {
        isSuccessfull = successStatus != 0
        this.data = data
        this.message = message
    }


}

the interface is like this:

interface LakuinAPI {

    @FormUrlEncoded
    @POST("processlogin")
    fun performLogin(
        @Field("kode_customer") outletCode: String,
        @Field("password") password: String
    ): Call<ServerData>


}

and then I use it in the activity, like the code below:

private fun sendLoginDataToServer(outletCode: String, password: String) {

        val call = lakuinAPI.performLogin(outletCode,password)

        call.enqueue(object: Callback<ServerData> {

            override fun onFailure(call: Call<ServerData>, t: Throwable) {
                Toast.makeText(this@LoginActivity,t.localizedMessage,Toast.LENGTH_LONG).show()
            }

            override fun onResponse(call: Call<ServerData>, response: Response<ServerData>) {

                if (!response.isSuccessful) {
                    Toast.makeText(this@LoginActivity,"Code: " + response.code(),Toast.LENGTH_LONG).show()
                    return
                }

                val lakuinServerData = response.body()
                val userList = lakuinServerData?.data as List<User> // the error in here
                val userData = userList.first() // the error in here
                println(userData)

            }

        })


    }

but I get error message:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.ssss.lakuinkotlin.Model.User

I give comment in the code above the location of the error. I don't why it happened.

to be honest, I am not if this is the correct way to catch user data from general response JSON like the the JSON above. is there a better way ?

sarah
  • 3,819
  • 4
  • 38
  • 80
  • Does [this](https://stackoverflow.com/questions/32444863/google-gson-linkedtreemap-class-cast-to-myclass) help ? – Vucko Jan 23 '19 at 11:20

2 Answers2

1

You can use generics to achieve it

class Response<Data> constructor() : ResponseSimple() {
   @SerializedName(FIELD_DATA)
   var data: Data? = null
   private constructor(data: Data) : this() {
       this.data = data
   }
   companion object {
       const val FIELD_SUCCESS = "success"
       const val FIELD_ERROR = "error"
       const val FIELD_DATA = "data"
       const val FIELD_MESSAGE = "message"

       @JvmStatic
       fun <Data> create(data: Data): Response<Data> {
          return Response(data)
       }
   }
}

And ResponseSimple is

open class ResponseSimple {

@SerializedName(Response.FIELD_ERROR)
var error: String = ""

@SerializedName(Response.FIELD_SUCCESS)
var succes: Boolean = false

@SerializedName(Response.FIELD_MESSAGE)
var message:String = ""
}

Then api response should be Call<Response<ServerData>>.

And about ClassCastException, you can't convert ServerData to User just using as. You need to use Call<Response<ArrayList<User>>> or create class converter.

Stanislav Bondar
  • 6,056
  • 2
  • 34
  • 46
0

Try replacing this line :

val userList = lakuinServerData?.data as List<User> 

with:

val userList = lakuinServerData?.data as new TypeToken<List<User>>(){}.getType()
Muhammad Saad Rafique
  • 3,158
  • 1
  • 13
  • 21