0

I am using retrofit 2+ with gson library to fetch data from rest api. But whenever I try to run my app, it shows the following error:

"Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $".

Hence I tried a lot to indentify the problem. Here is my code for json serializer. Json is given this way. In some cases some field is missing, for example title is not included in all cases.

Api Data:

 [
    {
    "_id": "fhfh49879787989",
    "dn": "CN=9879798789",
    "whenChanged": "20170704065349.0Z",
    "name": "Student",
    "mail": "student@mail.com",
    "updated_at": "2017-07-04T18:22:43.624Z"
  },
  {
    "_id": "595bdcf32c67a3f9ee6c2a25",
    "dn": "CN=dsfdsfsdfsf",
    "givenName": "Accounting Office",
    "whenChanged": "20170801114732.0Z",
    "name": "Accounting",
    "mail": "accounting@mail.com",
    "updated_at": "2017-07-04T18:22:43.641Z"
  },
  {
    "_id": "584ab3b4122d13e1b0d1578d",
    "dn": "CN=sfdfsfsdfl",
    "sn": "Abels",
    "title": "Student",
    "givenName": "Gardrut",
    "whenChanged": "20170807150844.0Z",
    "department": "PMO",
    "company": "Multi Lmited",
    "name": "Mike Lizz",
    "mail": "mail@yahoo.com",
    "mobile": "+1321646498",
    "updated_at": "2016-12-09T13:37:56.175Z"
  },
  {
    "_id": "584ab3b3122d13e1b0d15735",
    "dn": "CN=xdfsdfsfsdf",
    "sn": "Acsdff",
    "title": "Software Engineer",
    "givenName": "Olin",
    "whenChanged": "20170810064841.0Z",
    "department": "Head",
    "company": "Private limited",
    "name": "James Oliver",
    "mail": "mail@gmail.com",
    "mobile": "+41228",
    "updated_at": "2016-12-09T13:37:55.813Z"
  },
  ....
 ]

POJO:

public class ColleagueModel implements Serializable {

    @Expose
    private String _id;
    @Expose
    private String dn;
    @Expose
    private String givenName;
    @Expose
    private String whenChanged;
    @Expose
    private String name;
    @Expose
    private String mail;
    @Expose
    private String updatedAt;
    @Expose
    private String sn;
    @Expose
    private String title;
    @Expose
    private String department;
    @Expose
    private String company;
    @Expose
    private String mobile; 

    // getter and setter...

My Service class:

public interface ColleagueApiService {
    @GET("/api/users")
    Call<List<ColleagueModel>> getAllColleagues();
}

Rest maneger class:

public class ColleagueRestManager {

    public ColleagueApiService mColleagueService;

    public ColleagueApiService getColleagueService() {

        if (mColleagueService == null) {
            Gson gson = new GsonBuilder()
                .setLenient()
                .serializeNulls()
                .create();

            Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constants.HTTP.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

            mColleagueService = retrofit.create(ColleagueApiService.class);
        }
        return mColleagueService;
    }
}

Adapter Class:

public class MyColleaguesAdapter extends RecyclerView.Adapter<MyColleaguesAdapter.ColleagueHolder> {

    public static String TAG = MyColleaguesAdapter.class.getSimpleName();

    private List<ColleagueModel> mColleague;

    public MyColleaguesAdapter() {
        mColleague = new ArrayList<>();
    }

    private List<MyColleagueModel> myColleagueModels;     

    public interface ColleagueListListener {    
    }

    public void addColleague(ColleagueModel colleague) {
        Log.d(TAG,colleague.getName());
        mColleague.add(colleague);
        notifyDataSetChanged();
    }

    // create new views (invoked by the layout manager)
    @Override
    public ColleagueHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //inflate a new colleague view
        View view = LayoutInflater
            .from(parent.getContext())
            .inflate(R.layout.colleage_row_layout,parent,false);
        return new ColleagueHolder(view);
    }

    @Override
    public void onBindViewHolder(ColleagueHolder holder, int position) {

        ColleagueModel currentColleague = mColleague.get(position);

        holder.colleagueName.setText(currentColleague.getName());
        holder.companyName.setText(currentColleague.getCompany());
        holder.jobTitle.setText(currentColleague.getTitle());
    }

    @Override
    public int getItemCount() {
        return mColleague.size();
    }

    public class ColleagueHolder extends RecyclerView.ViewHolder{
        public CardView cardView;
        public ImageView colleaguePicture;
        public TextView colleagueName;
        public TextView companyName;
        public TextView jobTitle;

    public ColleagueHolder(View itemView) {
        super(itemView);
        colleaguePicture = itemView.findViewById(R.id.colleague_picture);
        colleagueName = itemView.findViewById(R.id.colleague_name);
        companyName = itemView.findViewById(R.id.company_name);
        jobTitle = itemView.findViewById(R.id.job_title);
        cardView = itemView.findViewById(R.id.cardview_user);
    }
}

And Finally the Activity Class:

public class MyColleaguesPage extends AppCompatActivity  {

    private RecyclerView recyclerView;
    private MyColleaguesAdapter adapter;  
    private Controller mController;
    private ColleagueRestManager mManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mycolleagues_layout);

        // Showing and Enabling clicks on the Home/Up button
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
        }
        configViews();

        mManager = new ColleagueRestManager();
        Call<List<ColleagueModel>> colleagueList = mManager.getColleagueService().getAllColleagues();
        colleagueList.enqueue(new Callback<List<ColleagueModel>>() {
            @Override
            public void onResponse(Call<List<ColleagueModel>> call, Response<List<ColleagueModel>> response) {

                if(response.isSuccessful()) {
                    List<ColleagueModel> colleagueList = response.body();

                    for (int i = 0; i < colleagueList.size(); i++) {
                        ColleagueModel colleague = colleagueList.get(i);
                        adapter.addColleague(colleague);
                    }
                } else {
                    int sc = response.code();
                    switch (sc){
                        case 400:
                            Log.e("Error 400", "Bad Request");
                            break;
                        case 404:
                            Log.e("Error 404", "Not Found");
                            break;
                        default:
                            Log.e("Error", "Generic Error");
                    }
                }
            }

            @Override
            public void onFailure(Call<List<ColleagueModel>> call, Throwable t) {
                Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
                Log.d("Error Message:"+"",t.getMessage());
            }
        })    
    }

    private void configViews() {
        recyclerView = this.findViewById(R.id.colleagues_recycler);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(MyColleaguesPage.this));
        recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());

        adapter = new MyColleaguesAdapter();
        recyclerView.setAdapter(adapter);
    }
}

Conastant class for getting api:

public class Constants {
    public static final class HTTP {
        public static final String BASE_URL = "https://app.com"; //not given the real url
    }

    public static final class DATABASE {
    }
}

Edited Body

    08-20 07:51:02.334 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: --> GET https://app.blu-pa.com/api/users http/1.1
08-20 07:51:02.334 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: --> END GET
08-20 07:51:02.377 1346-1367/? W/audio_hw_generic: Not supplying enough data to HAL, expected position 860742 , only wrote 707760
08-20 07:51:02.383 1346-1366/? W/audio_hw_generic: Not supplying enough data to HAL, expected position 707956 , only wrote 707760
08-20 07:51:02.386 3421-3426/demo.app.com.bluapp_client_and I/art: Do partial code cache collection, code=59KB, data=59KB
08-20 07:51:02.387 3421-3426/demo.app.com.bluapp_client_and I/art: After code cache collection, code=50KB, data=54KB
08-20 07:51:02.387 3421-3426/demo.app.com.bluapp_client_and I/art: Increasing code cache capacity to 256KB
08-20 07:51:02.392 3421-3440/demo.app.com.bluapp_client_and D/EGL_emulation: eglMakeCurrent: 0xa494e540: ver 2 0 (tinfo 0xa494d430)
08-20 07:51:02.404 1581-1601/? I/ActivityManager: Displayed demo.app.com.bluapp_client_and/.activity.myColleague.MyColleaguesPage: +83ms

    08-20 07:51:02.657 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: <-- 200  https://app.blu-pa.com/login?callback=%2F (322ms)
08-20 07:51:02.657 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: x-dns-prefetch-control: off
08-20 07:51:02.657 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: x-frame-options: SAMEORIGIN
08-20 07:51:02.657 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: strict-transport-security: max-age=15552000; includeSubDomains
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: x-download-options: noopen
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: x-content-type-options: nosniff
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: x-xss-protection: 1; mode=block
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: set-cookie: language=de-DE; Max-Age=86400; Path=/; Expires=Mon, 21 Aug 2017 07:51:05 GMT
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: set-cookie: connect.sid=s%3Ah-1Pju1IRtwJwNG5VU5Ja0t1OPujcaHz.Bd6gMWB8Q3DtgOpP%2BHnMuh6QY5VGHwcChbOqTRVVee0; Path=/; HttpOnly
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: content-type: text/html; charset=utf-8
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: etag: W/"8af-hp6ns0Ui95kDbe9rVovQHfKz5pA"
08-20 07:51:02.658 3421-5002/demo.app.com.bluapp_client_and D/OkHttp: vary: Accept-Encoding
  • 1
    Again.. for sure response is not the same as you posted. Use an interceptor and print the body. See here https://stackoverflow.com/questions/33053541/retrofit-2-0-onfailure-raw-response/45706709#45706709 – Cătălin Florescu Aug 19 '17 at 23:32
  • @Florescu I have used okhttp and print the body on my edited question –  Aug 20 '17 at 07:51

1 Answers1

0

As i see in your retrofit log, there is no your expected answer. You make request whitout sending token or etag, depending server implementation. Usually, etag is used when you edit some personal informations.

There is a redirect to login page: https://app.blu-pa.com/login?callback=%2F (322ms).

Retrofit is retrieveing you the html (Retrofit return html as a string in body) for login page (D/OkHttp: content-type: text/html; charset=utf-8), that's why you get "Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $".

Add in header the token that you get from login in your users request are you are ready to go.

Edit

You should use an interceptor to add your token to each request

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();  
httpClient.addInterceptor(new Interceptor() {  
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request original = chain.request();

        Request request = original.newBuilder()
            .header("Authorization", "YourTokenHere")
            .method(original.method(), original.body())
            .build();

        return chain.proceed(request);
    }
}

OkHttpClient client = httpClient.build(); 
Retrofit retrofit = new Retrofit.Builder().client(client).build();

Now it's up to you how you store and retrieve token.

You can read more here about incerceptor.

Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36
  • Add in header the token that you get from login in your users request , Could you please explain more about that. How can I add token. In which place should I addd this –  Aug 20 '17 at 08:26
  • It would be really helpful for me if you kindly tell me where can I set header here –  Aug 20 '17 at 08:29
  • in with portion in my code Shall I use it. Sorry I am still very new in android developing. I tried to write inside ColleagueRestManager but, now sure. If you do not mind could you please tell me based on my code –  Aug 20 '17 at 08:53
  • what do you mean by original.method(), original.body() –  Aug 20 '17 at 08:54
  • Is the actual request that you are trying to make. Before you send a request, you intercept it, add the header and then the request is made. You can read the documentation for more informations. Actually, you should read every time the documentation before post a question, do more investigation. – Cătălin Florescu Aug 20 '17 at 08:58
  • I already get authentictae from the login page by oauth2 method. Yesterday at first I tried with retrofit 1. with okHttp. I got perfect result. and my recyclerview shows all the reasult from server. But after that I tried to implement with retrofit 2+. with Gson. Now I am facing too much problem. In my point of view. if it works with retrofit 1. then ther server is alright. May be I am doing a mistake with retrofit 2. –  Aug 20 '17 at 09:02
  • @tamrezh21 Also, unauthenticate is returning 403 error status code. You check for some of them. That's why you don't see nothing in your log. – Cătălin Florescu Aug 20 '17 at 11:47