Retrofit returning null response Android - java

I'm using Retrofit to make API call, When I handle the response I get the next error (Need to get the data from the API call) -
Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
I don't know if I'm doing it right. Anyway here's my code.
Here's the url link: https://data.gov.il/api/
Retrofit call -
#GET("datastore_search?resource_id=2c33523f-87aa-44ec-a736-edbb0a82975e")
Call<Result> getRecords();
Retrofit base call -
private static Retrofit retrofit;
public static final String BASE_URL = "https://data.gov.il/api/action/";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Model class -
public class Result {
#SerializedName("include_total")
#Expose
private Boolean includeTotal;
#SerializedName("resource_id")
#Expose
private String resourceId;
#SerializedName("fields")
#Expose
private List<Field> fields = null;
#SerializedName("records_format")
#Expose
private String recordsFormat;
#SerializedName("records")
#Expose
private List<Record> records = null;
#SerializedName("limit")
#Expose
private Integer limit;
#SerializedName("_links")
#Expose
private Links links;
#SerializedName("total")
#Expose
private Integer total;
public Boolean getIncludeTotal() {
return includeTotal;
}
public void setIncludeTotal(Boolean includeTotal) {
this.includeTotal = includeTotal;
}
public String getResourceId() {
return resourceId;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
public List<Field> getFields() {
return fields;
}
public void setFields(List<Field> fields) {
this.fields = fields;
}
public String getRecordsFormat() {
return recordsFormat;
}
public void setRecordsFormat(String recordsFormat) {
this.recordsFormat = recordsFormat;
}
public List<Record> getRecords() {
return records;
}
public void setRecords(List<Record> records) {
this.records = records;
}
...
Main Activity -
RecallService service = RetrofitClientInstance.getRetrofitInstance().create(RecallService.class);
Call<Result> records = service.getRecords();
records.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Log.d(TAG, String.valueOf(response.body().getRecords().get(0).getId())); // ERROR
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.e(TAG, t.getMessage());
}
});

The response, which you are getting from the API, doesn't fit the Result POJO.
The response you get from API is like below:
{
"help": "https://data.gov.il/api/3/action/help_show?name=datastore_search",
"success": true,
"result": {...}
}
By using the Result POJO, you are assuming that you get the response as below, which is a json inside the actual response json, and is not what you actually receive. So, just create a POJO which fairly represents the actual response.
{
"include_total": true,
"resource_id": "2c33523f-87aa-44ec-a736-edbb0a82975e",
"fields": [...],
"records_format": "objects",
"records":[...]
}
Try making a class like below (set the annotations yourself):
class Resp{
Result result;
}
Replace the class Result with Resp, like below and other usages:
#GET("datastore_search?resource_id=2c33523f-87aa-44ec-a736-edbb0a82975e")
Call<Resp> getRecords();
Then, finally you can do:
response.body().getResult().getRecords()

The API link you've shared returns the response in the format below:
{"help": "https://data.gov.il/api/3/action/help_show?name=datastore_search", "success": true, "result": {...}}
You are setting the response object to be of type Result which is actually a sub-element within the root element help in the json response. response.body() would include help and the result would be it's sub-element. Since it is not parsed correctly, you're getting a null response.
You will need to include the root element in your model class and update the API call to use that class type as the response type.

Related

How to post data from postman using a static list and also get all data

I am new to spring boot, I want to post the data(let's say question data) using a static list without using database and repository, but i can't understand how to solve it.
This my controller class
#PostMapping
public ResponseEntity<Object> createQuestion(#Valid #RequestBody CreateQuestionCommand command) {
var query = new FindQuestionByIdTemp(command.getId());
var response = queryGateway.query(query, ResponseTypes.instanceOf(Object.class)).join();
if (response != null) {
return new ResponseEntity<>(new QuestionCreatedResponse(command.getId(),"This id is already associated with other question"), HttpStatus.INTERNAL_SERVER_ERROR);
}
try {
commandGateway.send(command);
return new ResponseEntity<>(new QuestionCreatedResponse(command.getId(), "Question Created successfully!"), HttpStatus.CREATED);
} catch (Exception e) {
var safeErrorMessage = "Error while processing create question request for id - " + command.getId();
System.out.println(e.toString());
return new ResponseEntity<>(new QuestionCreatedResponse(command.getId(), safeErrorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CreateQuestionCommandClass
public class CreateQuestionCommand {
#AggregateIdentifier
private String id;
private Questions question;
}
Questions Class
public class Questions {
#Id
private String id;
private String title;
private String label;
private String feedback;
}
This is an AggregateClass
#Aggregate
public class QuestionAggregateClass {
#AggregateIdentifier
private String id;
private Questions questions;
public QuestionAggregateClass() {
}
#CommandHandler
public QuestionAggregateClass(CreateQuestionCommands command) {
var newQuestion=command.getQuestions();
newQuestion.setId(command.getId());
var event = QuestionCreatedEvents.builder()
.id(command.getId())
.questions(newQuestion)
.build();
AggregateLifecycle.apply(event);
}
#EventSourcingHandler
public void on(QuestionCreatedEvents event){
this.id=event.getId();
this.questions=event.getQuestions();
}
}
Here i want to make a static list so that when i post data from postman, it will save in that static list and i am able to getData from here also. But i dont know how to make that fucnction while using lambda expression. I dont want to write dummy data manually here, i want to store it from postman sending post request
public class QuestionDataAsList {
public static List<Questions> questions;
public static void save(Questions questions) {
}
}
And want to make implementation of event here, and want to call static list here.
#Component
public class QuestionEventHandlerImp implements QuestionEventHandler{
#Override
public void on(QuestionCreatedEvents event) {
QuestionDataAsList.save(event.getQuestions());
}

why do we use a call list with retrofit

I have an example ,I want to understand some parts,In this exampl ,It was working fine ,but when I changed part
from:
call list<model>> method();
to:
call <model> method();
It caused an error ,What's the reason for that?
What is the difference between the two cases?
// MainActivity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GetData service = RetrofitClient.getRetrofitInstance().create(GetData.class);
Call<RetroUsers> call = service.getAllUsers();
call.enqueue(new Callback<RetroUsers>() {
#Override
public void onResponse(Call<RetroUsers> call, Response<RetroUsers> response) {
Log.i("print", "" + response.body().getUser());
}
#Override
public void onFailure(Call<RetroUsers> call, Throwable t) {
Log.i("print", "Dont" + t.getMessage());
}
});
}
///Error message :
I/print: Dontjava.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
// interface GetData:
public interface GetData {
#GET("/users")
Call<RetroUsers>getAllUsers();
/*
#GET("/users")
Call<List<RetroUsers>> getAllUsers();
*/
}
// RetrofitClient :
public class RetrofitClient {
private static Retrofit retrofit;
private static final String BASE_URL = "https://jsonplaceholder.typicode.com";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
// model class :
public class RetroUsers {
#SerializedName("name")
private String name;
public RetroUsers(String name) {
this.name = name;
}
public String getUser() {
return name;
}
public void setUser(String name) {
this.name = name;
}
}
This: Call<RetroUsers> getAllUsers();
will cause you an error because the getAllUsers() will return more than one records of RetroUsers. Due to which it requires you to provide it a type as List so that its datatype is set to List and it can then handle multiple records.
Go through basics of Core Java to better understand this.
In one case you tell the deserializer that you expect a single Model, in the other case you tell it to expect a list of models, aka a List<Model>. Depending on what data you actually get you need to use one or the oter.
Of course you can "hide" the List<...> within your model by not using List<Model> but:
public class MoreThanOneModel {
public List<Model> entries;
...
}
But that does not change the underlying reasoning.

How to handle different schema for errors in Retrofit 2?

I have an API which returns one of the following schemas:
Success (data found)
{
item_count: 83,
items_per_page: 25,
offset: 25,
items: [
{ ... },
{ ... },
{ ... },
...
]
}
Failure (no data found)
{
success: false,
error: {
code: 200,
message: "Server is busy"
}
}
I want to use Retrofit 2 with GSON to build a wrapper around this API and convert to POJOs, however I'm uncertain how to handle the fact that the API potentially returns two entirely different schemas. For now, I have the following classes:
public class PaginatedResponse<T> {
private int item_count;
private int items_per_page;
private int offset;
private List<T> items;
public PaginatedResponse<T>(int item_count, int items_per_page, int offset, List<T> items) {
this.item_count = item_count;
this.items_per_page = items_per_page;
this.offset = offset;
this.items = items;
}
public List<T> getItems() {
return this.items;
}
}
public class Item {
private int id;
private String name;
// ...
}
Then for my API interface I have:
public interface API {
#GET("items")
Call<PaginatedResponse<Item>> getItems();
}
Then finally to use this I tried to say:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost")
.addConverterFactory(GsonConverterFactory.create())
.build();
API api = retrofit.create(API.class);
api.getItems().enqueue(new retrofit2.Callback<PaginatedResponse<Broadcast>>() {
#Override
public void onResponse(Call<PaginatedResponse<Broadcast>> call, Response<PaginatedResponse<Broadcast>> response) {
Log.d("SUCCESS", response.body().getItems().toString());
}
#Override
public void onFailure(Call<PaginatedResponse<Broadcast>> call, Throwable t) {
Log.d("FAILURE", t.toString());
}
}
So long as no errors are thrown, this seems to work. But when an error is thrown, I get the following in Logcat and my app crashes:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
It seems like because the failure JSON lacks an items property, it's setting List<Item> items to null
It seems like because the failure JSON lacks an items property, it's setting List items to null
Yes, It is. You're getting NullPointerException , because you called toString() on a null object. This is an expected behavior.
Solution
As you have different schema for error and success, you need to create a model with both values. Below given a minimal example,
ResponseModel.java
class ResponseModel {
// ERROR
private final Boolean success;
private final Error error;
// SUCCESS
private final int item_count;
// more success values...
ResponseModel(Boolean success, Error error, int item_count) {
this.success = success;
this.error = error;
this.item_count = item_count;
}
public class Error {
private final int code;
private final String message;
private Error(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
public Boolean getSuccess() {
return success;
}
public Error getError() {
return error;
}
public int getItem_count() {
return item_count;
}
}
and in onResponse method, you can check if the response is success or not like this
ResponseModel responseModel = response.body();
if (responseModel.getError() == null) {
// success
doSomethingWithSuccess(responseModel.getItem_count())
} else {
// error
doSomethingWithError(responseModel.getError())
}

java.lang.IllegalStateException in retrofit

I am using retrofit 2.0 Gson data not getting
When i check Postman i am getting response below
{
"name": "Yashodhan Communication",
"zone": "9-Belgaum",
"tsm_name": "Tarun Patil",
"asm_name": "Shivakumar Patil"
}
Error:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was
BEGIN_OBJECT at line 1 column 2 path $
APIClient Client
public static Retrofit getClient() {
retrofit = new Retrofit.Builder()
.baseUrl("http://vehiclerescue.in/ideadarpan_beta/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
This is Pojo class
public class Appuser_Pojoclass {
#SerializedName("name")
#Expose
private String name;
#SerializedName("zone")
#Expose
private String zone;
#SerializedName("tsm_name")
#Expose
private String tsm_name;
#SerializedName("asm_name")
#Expose
private String asm_name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
public String getTsm_name() {
return tsm_name;
}
public void setTsm_name(String tsm_name) {
this.tsm_name = tsm_name;
}
public String getAsm_name() {
return asm_name;
}
public void setAsm_name(String asm_name) {
this.asm_name = asm_name;
}
}
Activity class
private void fetchAllAppUserdata()
{
progressBar.setVisibility(View.VISIBLE);
Log.d("getauthkeydisplay","**** "+Idea_Urban.getInstance().get_Authkeyvalues());
ideaInterface.get_AppUsers(Idea_Urban.getInstance().get_Authkeyvalues()).enqueue(new Callback<List<Appuser_Pojoclass>>() {
#Override
public void onResponse(Call<List<Appuser_Pojoclass>> call, Response<List<Appuser_Pojoclass>> response) {
if(app_users != null)
{
progressBar.setVisibility(View.INVISIBLE);
app_users.clear();
}
if(response.body()!=null) {
app_users.addAll(response.body());
Log.d("appuserresponce","***** "+response.body());
// app_userDetailsAdapter.notifyDataSetChanged();
}else
{
progressBar.setVisibility(View.INVISIBLE);
}
}
#Override
public void onFailure(Call<List<Appuser_Pojoclass>> call, Throwable t) {
Log.d("failure","**** ");
Log.d("printmetthodsfailure","faiure"+call.toString() + "\n " + t.toString());
progressBar.setVisibility(View.INVISIBLE);
}
});
}
I found some solution as google suggestion but i donot know how to achieve.
You problem is java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
So you should check you response of your code .
Your JSON response is JSONObject ,so you should use Call<Appuser_Pojoclass> call
Change
Call<List<Appuser_Pojoclass>> call
to
Call<Appuser_Pojoclass> call
The error is in the Call declaration,change
Call<List<Appuser_Pojoclass>>
with
Call<Appuser_Pojoclass>
Your retrofit call response type is <List<Appuser_Pojoclass>> but your API response is not a list of Appuser_Pojoclass, it is simply a single object of type Appuser_Pojoclass.
Change type from <List<Appuser_Pojoclass>> to Appuser_Pojoclass in your retrofit call
You probably defined your get_AppUsers method as something that returns Call<List<Appuser_Pojoclass>> instead of just Call<Appuser_Pojoclass> so it ends up looking for a JSON array, but then your response only contains a single JSON object which causes the error. Change get_AppUsers to return Call<Appuser_Pojoclass> and then change your callback accordingly and that should fix the problem.

Retrofit2, Android, #Get Parsing for the Array of Array

I am parsing the title correctly and displaying it in a listview. I cant seem to access the
String findMe;
seen below. Here are the two objects, sample response and my call.
response pojo:
public class Response {
public String count;
public Result []results;
}
first object
public class Result {
public String title;
public static arr [] Details;
second object
public class Details {
public Integer _id;
public String findMe;
}
response:
Call<Response> call = api.getListWith(API_KEY);
call.enqueue(new Callback<Response>() {
#Override
public void onResponse(Call<Response> call, Response<Response> response) {
result = response.body();
}
I am getting the title just by passing 'result' into the adapter and using
result[i].getTitle();
I tried using
result[i].Details[0].findMe;
but my error response is:
java.lang.NullPointerException: Attempt to read from null array
public class Result {
public String title;
public Details[] arr;
}
Then
results[i].getarr[0].getfindMe();

Categories