How to fix "Expected a string but was BEGIN_OBJECT" in GSON

You might have come across a gson exception that says  "Expected a string but was BEGIN_OBJECT". This means that you are parsing the response as if it were a string, but actually it is a json object.

I would recommend you to check how to parse json in android if you have no prior knowledge in parsing json

Add these dependencies in your app's build.gradle file

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

Suppose your response is a json object, something like the following

{ 
"username":"jon",
"email":"jon@email.com", 
"user_array": [
 { 
 "user_address":"jon",
 "user_location":"jon@email.com"
 }, {..},
 .
 . 
 ]
}

You will receive the error Expected a string but was BEGIN_OBJECT if you try to parse it like this
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("base_url")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RequestInterface request = retrofit.create(RequestInterface.class);
Call<String> call=request.getJson();
call.enqueue(new Callback<String>() {
    @Override    
    public void onResponse(Call<String> call, Response<String> response) {
        progressDialog.dismiss();
        String s= String.valueOf(response.get("username"));
        JsonArray user_array= response.getAsJsonArray("user_array");
        Toast.makeText(PrintTicket.this,response.toString(),Toast.LENGTH_SHORT).show();
    }

    @Override    
    public void onFailure(Call<String> call, Throwable t) {
        progressDialog.dismiss();
        Toast.makeText(PrintTicket.this,t.toString(),Toast.LENGTH_SHORT).show();
    }
});

GSON will throw the above mentioned error because you have used Call<Stringinstead of using Call<JsonObject> or  using a pojo class

Solution 1 : by using JsonObject:


Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("base_url")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RequestInterface request = retrofit.create(RequestInterface.class);
Call<JsonObject> call=request.getJson();
call.enqueue(new Callback<JsonObject>() {
    @Override    
    public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
        progressDialog.dismiss();
        String s= String.valueOf(response.get("username"));
        JsonArray user_array= response.getAsJsonArray("user_array");
        Toast.makeText(PrintTicket.this,response.toString(),Toast.LENGTH_SHORT).show();
    }

    @Override    
    public void onFailure(Call<JsonObject> call, Throwable t) {
        progressDialog.dismiss();
        Toast.makeText(PrintTicket.this,t.toString(),Toast.LENGTH_SHORT).show();
    }
});

You can see that here we are expecting a JsonObject as response parameter instead of String. Now we can parse the response correctly as we are expecting the same format mentioned in the response. Also dont forget to change the response param in interface to Call<JsonObject> as shown below

RequestInterface.java
public interface RequestInterface {

@GET("api_endpoint")
Call<JsonObject> getJson();

}


Solution 2 : by using POJO class

First create a pojo class like this

Example.java
public class Example {

@SerializedName("username")
@Expose
private String username;
@SerializedName("email")
@Expose
private String email;
@SerializedName("user_array")
@Expose
private List<UserArray> userArray = null;

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public List<UserArray> getUserArray() {
return userArray;
}

public void setUserArray(List<UserArray> userArray) {
this.userArray = userArray;
}

}

Then use it inside your retrofit call like this

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("base_url")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RequestInterface request = retrofit.create(RequestInterface.class);
Call<Example> call=request.getJson();
call.enqueue(new Callback<Example>() {
    @Override    
    public void onResponse(Call<Example> call, Response<Example> response) {
        progressDialog.dismiss();
        String user_name= response.getUsername();
        user_array= new ArrayList<>(response.getUserArray());
        Toast.makeText(PrintTicket.this,response.toString(),Toast.LENGTH_SHORT).show();
    }

    @Override    
    public void onFailure(Call<Example> call, Throwable t) {
        progressDialog.dismiss();
        Toast.makeText(PrintTicket.this,t.toString(),Toast.LENGTH_SHORT).show();
    }
});

RequestInterface.java
public interface RequestInterface {
@GET("api_endpoint")
Call<Example> getJson();
}

You can use Call<String> as response param only if your response is not enclosed in curly brackets. In this case the response will be a plain text

No comments:

Post a Comment