Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY

89,250

Solution 1

Right now you are parsing the response as if it was formatted like this:

{
  "contacts": [
    { .. }
  ]
}

The exception tells you this in that you are expecting an object at the root but the real data is actually an array. This means you need to change the type to be an array.

The easiest way is to just use a list as the direct type in the callback:

@GET("/users.json")
void contacts(Callback<List<User>> cb);

Solution 2

dependencies used :

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

json responses can be an array response or an object response or even a combination of both. See the following three cases

Case 1 : Parsing a json array response (OP's case)

This case applies to those json responses which are of the form [{...} ,{...}]

E.g.

[
  {
    "id": 3,
    "username": "jezer",
    "regid": "oiqwueoiwqueoiwqueoiwq",
    "url": "http:\/\/192.168.63.175:3000\/users\/3.json"
  },
  .
  .
]

First create a model class for this array or just goto jsonschema2pojo and auto generate one like below

Contacts.java

public class Contacts {

@SerializedName("id")
@Expose
private Integer id;
@SerializedName("username")
@Expose
private String username;
@SerializedName("regid")
@Expose
private String regid;
@SerializedName("url")
@Expose
private String url;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

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

public String getRegid() {
return regid;
}

public void setRegid(String regid) {
this.regid = regid;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

}

ContactsInterface

In this case you should return a list of objects like the following

public interface ContactsInterface {
@GET("/users.json")
Call<List<Contacts>> getContacts();
}

Then make the retrofit2 call like the following

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("baseurl_here")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ContactsInterface request = retrofit.create(ContactsInterface.class);
    Call<List<Contacts>> call = request.getContacts();
    call.enqueue(new Callback<List<Contacts>>() {
        @Override
        public void onResponse(Call<List<Contacts>> call, Response<List<Contacts>> response) {
            Toast.makeText(MainActivity.this,response.body().toString(),Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(Call<List<Contacts>> call, Throwable t) {
            Log.e("Error",t.getMessage());
        }
    });

response.body() will give you the list of objects

YOU MAY ALSO CHECK THE FOLLOWING TWO CASES FOR REFERENCE

Case 2 : Parsing a json object response

This case applies to those json responses which are of the form {..}

E.g.

{
"id": 3,
"username": "jezer",
"regid": "oiqwueoiwqueoiwqueoiwq",
"url": "http:\/\/192.168.63.175:3000\/users\/3.json"
}

Here, we have the same object as above example. So the model class will be the same, but like above example we dont have an array of these objects - just one single object and hence we dont need to parse it as a list.

So make the following changes for an object response

public interface ContactsInterface {
    @GET("/users.json")
    Call<Contacts> getContacts();
    }

Then make the retrofit2 call like the following

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("baseurl_here")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ContactsInterface request = retrofit.create(ContactsInterface.class);
    Call<Contacts> call = request.getContacts();
    call.enqueue(new Callback<Contacts>() {
        @Override
        public void onResponse(Call<Contacts> call, Response<Contacts> response) {
            Toast.makeText(MainActivity.this,response.body().toString(),Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(Call<Contacts> call, Throwable t) {
            Log.e("Error",t.getMessage());
        }
    });

response.body() will give you the object

You may also check a common error while parsing json object response :"expected begin_array but was begin_object"

Case 3 : Parsing a json array inside json object

This case applies to those json responses which are of the form {"array_name":[{...} ,{...}]}

E.g.

    {
    "contacts": 
         [
            {
             "id": 3,
             "username": "jezer",
             "regid": "oiqwueoiwqueoiwqueoiwq",
             "url": "http:\/\/192.168.63.175:3000\/users\/3.json"
            }
         ]
    }

You will need two model classes here since we have two objects(one outside and one inside the array).Generate it like below

ContactWrapper

public class ContactWrapper {

@SerializedName("contacts")
@Expose
private List<Contacts> contacts = null;

public List<Contacts> getContacts() {
return contacts;
}

public void setContacts(List<Contacts> contacts) {
this.contacts = contacts;
}

}

You can use Contacts.java generated above for the list objects(generated for case 1)

So make the following changes for an object response

public interface ContactsInterface {
    @GET("/users.json")
    Call<ContactWrapper> getContacts();
    }

Then make the retrofit2 call like the following

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("baseurl_here")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ContactsInterface request = retrofit.create(ContactsInterface.class);
    Call<ContactWrapper> call = request.getContacts();
    call.enqueue(new Callback<ContactWrapper>() {
        @Override
        public void onResponse(Call<ContactWrapper> call, Response<ContactWrapper> response) {
            Toast.makeText(MainActivity.this,response.body().getContacts().toString(),Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(Call<ContactWrapper> call, Throwable t) {
            Log.e("Error",t.getMessage());
        }
    });

Here, the difference from case 1 is that we should use response.body().getContacts() instead of response.body() to get the list of objects

Some references for above cases :

case 1 : Parsing a json array response, case 2 : Parsing a json object response, mixed : Parsing json array inside another json object

Solution 3

in your interface replace

@GET("/users.json")
void contacts(Callback<Contacts> cb);

By this code

@GET("/users.json")
void contacts(Callback<List<Contacts>> cb);

Solution 4

Convert it into a list.

Below is the example:

BenchmarksListModel_v1[] benchmarksListModel = res.getBody().as(BenchmarksListModel_v1[].class);

Solution 5

Source Code Working

https://drive.google.com/open?id=0BzBKpZ4nzNzUVFRnVVkzc0JabUU

public interface ApiInterface {
    @GET("inbox.json")
    Call<List<Message>> getInbox();
}

 call.enqueue(new Callback<List<Message>>() {
            @Override
            public void onResponse(Call<List<Message>> call, Response<List<Message>> response) {

        YourpojoClass.addAll(response.body());

                mAdapter.notifyDataSetChanged();
            }

            @Override
            public void onFailure(Call<List<Message>> call, Throwable t) {
                Toast.makeText(getApplicationContext(), "Unable to fetch json: " + t.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
Share:
89,250

Related videos on Youtube

Jezer Crespo
Author by

Jezer Crespo

Updated on May 10, 2020

Comments

  • Jezer Crespo
    Jezer Crespo about 4 years

    I'm fairly new to JSON parsing, I'm using the Retrofit library of Square and ran into this problem.

    I'm trying to parse this JSON reponse:

    [
          {
            "id": 3,
            "username": "jezer",
            "regid": "oiqwueoiwqueoiwqueoiwq",
            "url": "http:\/\/192.168.63.175:3000\/users\/3.json"
          },
          {
            "id": 4,
            "username": "emulator",
            "regid": "qwoiuewqoiueoiwqueoq",
            "url": "http:\/\/192.168.63.175:3000\/users\/4.json"
          },
          {
            "id": 7,
            "username": "test",
            "regid": "ksadqowueqiaksj",
            "url": "http:\/\/192.168.63.175:3000\/users\/7.json"
          }
    ]
    

    Here are my models:

    public class Contacts {
    
        public List<User> contacts;
    
    }
    

    ...

    public class User {
    
        String username;
        String regid;
    
        @Override
        public String toString(){
            return(username);
        }  
    
    }
    

    my Interface:

    public interface ContactsInterface {
    
        @GET("/users.json")
        void contacts(Callback<Contacts> cb);
    
    }
    

    my success method:

    @Override
    public void success(Contacts c, Response r) {
        List<String> names = new ArrayList<String>();
        for (int i = 0; i < c.contacts.size(); i++) {
            String name = c.contacts.get(i).toString();
            Log.d("Names", "" + name);
            names.add(name);
        }
        ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item, names);
        mSentTo.setAdapter(spinnerAdapter);
    }
    

    When I use it on my success method it throws the error

    Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column2

    What is wrong here?

  • Douglas Mesquita
    Douglas Mesquita about 7 years
    @AzlanJamal the same way
  • Peter
    Peter about 7 years
    this appears to be the same answer minus the description that was posted by Jake Wharton.
  • Francisco MEza
    Francisco MEza about 7 years
    It's true, I had the same error and before modifying the pojo I decided to create an arrayList and get the same result
  • sorak
    sorak about 6 years
    Please provide an explanation as to why this solves the problem
  • JoseDuin
    JoseDuin almost 6 years
    If the server response has a JSON format like this {[..]}, then retrofit can't iterate correctly, a prefix must be placed on the JSON, as Jake Wharton suggests. the final JSON would be like this {"data":[..]} and problem solved.
  • Swapna
    Swapna over 3 years
    hai i am trying to request getting data in 3rd format but i am continuously getting the same error can you please help me
  • Navneet Krishna
    Navneet Krishna over 3 years
    please provide more info about your code, also make sure your array name spelling in the model class matches exactly with that of response
  • Swapna
    Swapna over 3 years
    i have tried like this but i am getting response.body() as [ ] empty....no data in it please help me