Raymond Gitonga
Raymond Gitonga

Reputation: 171

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY retrofit, kotlin

I am trying to implement user register on my app using Retrofit, i however keep getting this error not sure whats wrong, java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY

This is the response from postman

{
"isSuccessful": true,
"message": "successful",
"user": {
    "name": "Jackline Jazz",
    "email": "[email protected]",
    "phone": "000000"
}

}

I have two model classes the User model class

data class User(
val name: String,
val email:String,
val phone:String

)

And the login response class

data class LoginResponse(
val isSuccessful:Boolean,
val message: String,
val user: List<User>

)

my Retrofit object

object RetrofitClient {

private const val BASE_URL = "http://10.0.2.2:7000/"

val instance: RetrofitApi by lazy {
    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

retrofit.create(RetrofitApi::class.java)
}

}

Retrofit api

@FormUrlEncoded
@POST("users/register")
fun userRegister(
    @Field("name") name: String,
    @Field("email") email: String,
    @Field("password") password: String,
    @Field("confirmPassword") confirmPassword: String
): Call<LoginResponse>

and my register class

RetrofitClient.instance.userRegister(name, email, password, confirmPassword)
            .enqueue(object : Callback<LoginResponse> {
                override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
                    Toast.makeText(applicationContext, t.message, Toast.LENGTH_LONG).show()`
                }

                override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
                    if (response.body()?.isSuccessful!!){

                        val intent = Intent(applicationContext, MainActivity::class.java)

                        startActivity(intent)

                    }else{
                        Toast.makeText(applicationContext, response.body()?.message, Toast.LENGTH_LONG).show()
                    }
                }

            })
    }
}

And if possible someone help me implement Kotlin coroutines

Upvotes: 1

Views: 306

Answers (2)

CommonsWare
CommonsWare

Reputation: 1006514

In your previous question, you were hitting a users/login endpoint. You created a LoginResponse that modeled the response from the server. There, users/login returns a List<User>, so LoginResponse had to be set up that way.

Now, you are hitting a users/register endpoint... but you are still trying to use LoginResponse. As you can see from your JSON, you are getting different JSON from the server, where there is only one user. As a result, you need a different response class (e.g., RegisterResponse) that models this new response:

data class RegisterResponse(
  val isSuccessful:Boolean,
  val message: String,
  val user: User
)

@FormUrlEncoded
@POST("users/register")
fun userRegister(
    @Field("name") name: String,
    @Field("email") email: String,
    @Field("password") password: String,
    @Field("confirmPassword") confirmPassword: String
): Call<RegisterResponse>

Upvotes: 3

Thomas Keller
Thomas Keller

Reputation: 6050

BEGIN_ARRAY means there is some JSON returned that contains a JSON Array ([]) where your object schema expects a JSON object ({}). Sometimes APIs return different responses based on the client they're dealing with, so it's better to debug the issue at hand in your code. One way of doing this would be to temporarily use Retrofit's scalar converter factory.

Add this dependency:

dependencies {
    implementation 'com.squareup.retrofit2:converter-scalars:<your-retrofit-version>'
}

and this code

val retrofit = new Retrofit.Builder()  
        .addConverterFactory(ScalarsConverterFactory.create())
        ...

and return a Callable<String> instead of your custom type and log what is actually returned from your server. Then you can act upon accordingly.

Upvotes: 0

Related Questions