KaiZ KaiZ - 1 month ago 14
JSON Question

Why am I getting a null object?

I'm trying to get JSON data from

https://newsapi.org/v1/articles
with Retrofit, but I'm new to it and it's been quite confusing so far.

From what I can tell, I've managed to establish a successful connection. However, for some reason I haven't been able to identify yet, Retrofit doesn't map the data from the JSON strings to my
NewsAPI
class.

I'll post the code that I'm currently using, and I'm hoping someone can help me figure this out. Thanks!

Activity.java
:

final TextView tvTest = (TextView) findViewById(R.id.tvTest);
String url = "https://newsapi.org";

NewsAPI_Interface client = NewsAPI_Adapter.createService(NewsAPI_Interface.class);
Call<List<NewsAPI>> call = client.getData("techcrunch", "APIkeygoeshere");

call.enqueue(new Callback<List<NewsAPI>>() {
@Override
public void onResponse(Call<List<NewsAPI>> call, Response<List<NewsAPI>> response) {
if (response.body() != null) {
tvTest.setText(response.body().toString());

for (NewsAPI news: response.body()) {
System.out.println(news.source + " (" + news.status + ")");
tvTest.setText(news.source + " (" + news.status + ")");
}
} else {
tvTest.setText("It's null, " + response.errorBody().toString() + ", " + call.request().url());
}
}

@Override
public void onFailure(Call<List<NewsAPI>> call, Throwable t) {
tvTest.setText("An error ocurred!");
}
});


NewsAPI.java
:

public class NewsAPI {
String status;
String source;

public NewsAPI (String status, String source) {
this.status = status;
this.source = source;
}
}


NewsAPI_Interface.java
:

public interface NewsAPI_Interface {
@GET("/v1/articles")
Call<List<NewsAPI>> getData(@Query("source") String source, @Query("api_Key") String api_key);
}


NewsAPI_Adapter.java
:

public class NewsAPI_Adapter {
public static final String API_BASE_URL = "https://newsapi.org";

private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

private static Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());

public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
}

Answer

From what I can tell, I've managed to establish a successful connection

Well, you are ignoring the Throwable here, where you error would be output.

@Override
public void onFailure(Call<List<NewsAPI>> call, Throwable t) {
    tvTest.setText("An error ocurred!");
}

The response you are expecting looks like this

{
    "status": "ok",
    "source": "techcrunch",
    ...

The { there starts an object, not a List, therefore, you should define your interface differently. Then the documentation says the API key is added to the URL with apiKey, so change that.

public interface NewsAPI_Interface {
    @GET("/v1/articles")
    Call<NewsAPI> getData(@Query("source") String source, @Query("apiKey") String api_key);
}

(You need to define another object for an Article class. But that is a Gson problem, not Retrofit)

Finally, I remember reading somewhere that response.body() should only be called once.

Call<NewsAPI> call = client.getData("techcrunch", "APIkeygoeshere");

call.enqueue(new Callback<NewsAPI>() {
    @Override
    public void onResponse(Call<List<NewsAPI>> call, Response<List<NewsAPI>> response) {
        final NewsAPI responseBody = response.body();
        if (responseBody != null) {
            tvTest.setText(responseBody.toString());

            String news = news.source + " (" + news.status + ")";
            Log.i("responseBody", news);
            tvTest.setText(news);

        } else {
            tvTest.setText("It's null, " + response.errorBody().toString() + ", " + call.request().url());
        }
    }

    @Override
    public void onFailure(Call<List<NewsAPI>> call, Throwable t) {
        tvTest.setText("An error ocurred!");
        Log.e("news Error", t.getMessage());
    }
});

Best of luck!