1

I'm trying to use an API that returns JSON, and I'm using Retrofit to get the response and parse it

the API retruns JSON like the following :

{
 "total": 1244881,
 "totalHits": 500,
 "hits": [
  {
   "id": 5205518,
   "pageURL": "https://pixabay.com/photos/landscape-sea-sky-clouds-sunset-5205518/",
   "type": "photo",
   "tags": "landscape, sea, sky",
   "previewURL": "https://cdn.pixabay.com/photo/2020/05/22/14/04/landscape-5205518_150.jpg",
   "webformatWidth": 640,
   "webformatHeight": 427,
    ...
    ...
  },
    ...
    ...
  ]
}

And for The GSON to convert this json into an Object I created two classes : the first is : RquestModel.java

public class RequestModel {

    @SerializedName("total")
    private long total;

    @SerializedName("totalHits")
    private long totalHits;

    @SerializedName("hits")
    private List<Image> hits;

    //Getters and setters down here...

}

and the second class for the array in the json is :

public class Image {

    @SerializedName("id")
    private long id;

    @SerializedName("pageURL")
    private String pageURL;

    @SerializedName("type")
    private String type;

    @SerializedName("tags")
    private String tags;

    @SerializedName("previewURL")
    private String previewURL;

    @SerializedName("previewWidth")
    private long previewWidth;

    @SerializedName("previewHeight")
    private long previewHeight;

    @SerializedName("webformatURL")
    private String webformatURL;

    @SerializedName("webformatWidth")
    private long webformatWidth;

    @SerializedName("webformatHeight")
    private long webformatHeight;

    @SerializedName("largeImageURL")
    private String largeImageURL;

    @SerializedName("imageWidth")
    private long imageWidth;

    @SerializedName("imageHeight")
    private long imageHeight;

    @SerializedName("imageSize")
    private long imageSize;

    @SerializedName("views")
    private long views;

    @SerializedName("downloads")
    private long downloads;

    @SerializedName("favorites")
    private long favorites;

    @SerializedName("likes")
    private long likes;

    @SerializedName("comments")
    private long comments;

    @SerializedName("user_id")
    private long userId;

    @SerializedName("user")
    private String user;

    @SerializedName("userImageURL")
    private String userImageURL;


    //Getters and Setters down here ..... 



}

And for the interface that the GSON converter uses is like this :

public interface ImageAPIService {

    @GET(".")
    public Call<RequestModel> getRequest();

}

and When I try to use the response from the Callback like this :

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

ImageAPIService api = retrofit.create(ImageAPIService.class);

Call<RequestModel> request = api.getRequest();

request.enqueue(new Callback<RequestModel>() {
    @Override
    public void onResponse(Call<RequestModel> call, Response<RequestModel> response) {
        mViewModel.setTxt(response.body().getTotal() + "");
    }

    @Override
    public void onFailure(Call<RequestModel> call, Throwable t) {

    }
});

that gives an Exception of type: java.lang.NullPointerException :

java.lang.NullPointerException: Attempt to invoke virtual method 'long com.mapps.pixabayclient.models.RequestModel.getTotal()' on a null object reference

I tried to debug it and everything seems fine to me, I don't know what I have missed. if someone could notice the mistake, I'd be very thankful.

And thanks for your help in advance.

Jame Kayne
  • 41
  • 1
  • 9
  • check if `response.isSuccessful()` first and log the results to see what's wrong. Also log the http response code to understand what's going on – denvercoder9 May 25 '20 at 22:02
  • Inside on response set `RequestModel req = response.body();` And then try to get `req.getTotal()` – Hritik Gupta May 25 '20 at 22:02
  • @sonnet, it's strange, it gives 400 error code and I don't know why I updated the post and put the interface that the GSON Converter uses to see of there's a mistake at that level. – Jame Kayne May 25 '20 at 22:16
  • this is potentially problematic `@GET(".")`. Can you post your url? Doesn't have to exact. example: `https://example.com/api/hits` or something – denvercoder9 May 25 '20 at 22:38
  • @sonnet here's the API URL https://pixabay.com/api/?key=xxxxxxxx – Jame Kayne May 25 '20 at 22:57
  • Please remove the comment with real api keys. Don't post your api keys publicly – denvercoder9 May 25 '20 at 22:58
  • @sonnet it's done!. – Jame Kayne May 25 '20 at 23:01
  • What is your API_URL string? Is it the same url with api key as the one you commented? – denvercoder9 May 25 '20 at 23:08
  • @sonnet yes it is, with the xxxxx is the key. https://pixabay.com/api/?key=xxxxxxxxxxxxxxxxxxxxxxx and with https at the beginning, for some reason it is removed here. – Jame Kayne May 25 '20 at 23:10
  • It would be nice if you delete the whole `hits` array from the json and relevant code from the model class. It would reduce the question size and future readers won't have to read a lot because they are quite irrelevant to the problem as we discovered. – denvercoder9 May 26 '20 at 00:03
  • 1
    @sonnet, Ok I go that. – Jame Kayne May 26 '20 at 00:06

2 Answers2

1

So, the problem as you already figured out is that with your setup, the key is getting removed from the request. This is the response when I make the request with your setup.

Response{protocol=http/1.1, code=400, message=Bad Request, url=https://pixabay.com/api/}

But it shouldn't be the case. According to this from Jake Wharton, if we use @GET("."), then the whole base_url becomes the request url. For some reason, that isn't the case for you. [Maybe if we log the request url using some interceptor then we can understand better what's going on (whether it's a retrofit issue or a backend issue)].

Anyways, one solution is to pass the api key for each request like the following:

@GET(".")
public Call<RequestModel> getRequest(@Query("key") String key);

and call the function like this:

api.getRequest("my_api_key")

Another solution is to hard-code the api key into @GET("api/?key=xxxxx") like you already did.

The better solution is to use okhttp interceptor to intercept the requests and add the api key query parameter in the url. With this solution, you won't have to pass the api key for each function call.

denvercoder9
  • 2,979
  • 3
  • 28
  • 41
  • 1
    Thanks mate for helping it is really strange that the code I made first didn't work for some reason, I'll look into it and make a follow up in this post for me detailed debugging. Thanks anyway, you helped a lot !. – Jame Kayne May 26 '20 at 00:03
0

The problem was in the @GET annotation, as I moved the api/?key inside the parenthesis of the GET annotation -> @GET("api/?key=xxxxxxx") but the problem now is that I should pass the key in every GET or any other request type.

is there a workaround to systematically pass the key in each request ?

Jame Kayne
  • 41
  • 1
  • 9