why do we use a call list with retrofit - java

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.

Related

Retrofit returning null response Android

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.

How to get data from an anonymous class?

While i was learning Dagger2 I made a naive service class that provides data assynchronously (in this case jokes from a funny api) but I encountered a problem and I kind of stuck with it. I'm using retrofit2 for requesting data from network.
But I can't figure out how to pull out the joke object retrieved from network (via response.body()), from anonymous internal class, into joke instance variable of the external class. I'm getting NullPointerException:
public class ChuckNorrisJokeService {
private Joke joke;
public String getJoke() {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.chucknorris.io")
.build();
JokeService jokeService = retrofit.create(JokeService.class);
Call<Joke> call = jokeService.provideJoke();
call.enqueue(new Callback<Joke>() {
#Override
public void onResponse(Call<Joke> call, Response<Joke> response) {
joke = response.body();
}
#Override
public void onFailure(Call<Joke> call, Throwable t) {
System.out.println(t.getMessage());
}
});
return joke.getContent();
}
}
The Joke class is a simple POJO:
public class Joke {
#SerializedName("value")
private String content;
public String getContent() {
return content;
}
}
P.S. When calling synchronously the result is successful. How can I achieve the same functionality asynchronously?
P.S.S. I read this but it doesn't work for me and is so dirty.
The stacktrace is this:
Exception in thread "main" java.lang.NullPointerException
at com.alic.ChuckNorrisJokeService.getJoke(ChuckNorrisJokeService.java:41)
at com.alic.Application.run(Application.java:11)
at com.alic.Main.main(Main.java:6)
The Application and Main classes are very simple:
public class Application {
private ChuckNorrisJokeService chuckNorrisJokeService;
public Application() {
this.chuckNorrisJokeService = new ChuckNorrisJokeService();
}
public void run() {
System.out.println(chuckNorrisJokeService.getJoke());
}
}
and the Main class:
public class Main {
public static void main(String[] args) {
Application app = new Application();
app.run();
}
}

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.

java.lang.IllegalArgumentException: FormUrlEncoded can only be specified on HTTP methods with request body (e.g., #POST)

I am trying to get data from database by GET method on API
Here is my coding
APIServive.Interface
public interface APIService {
#FormUrlEncoded
#GET("Event")
Call<ApiResponseModel> viewEvent();
}
EventModel.Java
public class EventModel {
#SerializedName("nama_event") String nama_event;
#SerializedName("jenis_event") String jenis_event;
#SerializedName("creator") String creator;
#SerializedName("deskripsi_event") String deskripsi_event;
#SerializedName("tanggal") String tanggal;
#SerializedName("status") String status;
public String getNama_event() {
return nama_event;
}
public String getJenis_event() {
return jenis_event;
}
public String getCreator() {
return creator;
}
public String getDesk_event() {
return deskripsi_event;
}
public String getTanggal_event() {
return tanggal;
}
public String getStatus() {
return status;
}
}
ViewActivity.Java
public class ViewEventActivity extends AppCompatActivity {
#OnClick(R.id.back_arrow)void balik(){
finish();
}
#BindView(R.id.search_acara)EditText searchEvent;
public static final String URL = "http://iseoo.id/rest_ci_iseoo/";
private List<EventModel> acara = new ArrayList<>();
RecyclerView.LayoutManager mlayoutManager;
private RecyclerViewAdapter viewAdapter;
#BindView(R.id.recyclerViewEvent)RecyclerView recyclerView;
#BindView(R.id.progress_bar)ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_event);
ButterKnife.bind(this);
viewAdapter = new RecyclerViewAdapter(ViewEventActivity.this, acara);
mlayoutManager = new LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(mlayoutManager);
recyclerView.setAdapter(viewAdapter);
Retrofit retrofit=new
Retrofit.Builder().baseUrl(URL).
addConverterFactory(GsonConverterFactory.create()).build();
APIService API = retrofit.create(APIService.class);
Call<ApiResponseModel> getData = API.viewEvent();
getData.enqueue(new Callback<ApiResponseModel>() {
#Override
public void onResponse(Call<ApiResponseModel> call,
Response<ApiResponseModel> response) {
progressBar.setVisibility(View.GONE);
acara = response.body().getResult();
viewAdapter = new
RecyclerViewAdapter(ViewEventActivity.this, acara);
recyclerView.setAdapter(viewAdapter);
viewAdapter.notifyDataSetChanged();
}
// }
#Override
public void onFailure(Call<ApiResponseModel> call, Throwable t) {
}
});}}
And When i run the application it gives this
Caused by: java.lang.IllegalArgumentException: FormUrlEncoded can only be
specified on HTTP methods with request body (e.g., #POST).
for method APIService.viewEvent
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:752)
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:743)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:185)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy0.viewEvent(Unknown Source)
at
com.example.lenovog480.iseooalpha.ViewEventActivity.onCreate
(ViewEventActivity.java:61)
at android.app.Activity.performCreate(Activity.java:6127)
at android.app.Instrumentation.callActivityOnCreate
(Instrumentation.java:1128)
at android.app.ActivityThread.performLaunchActivity
(ActivityThread.java:2630)
I've searched for this problems and i've tried to solve it, but until now i didnt got the right answers for this problem, if possible you can contact me cause i really need help
Please Anybody help me and save my life thanks :')
If you add #FromUrlEncoded to the top of #GET, you will have java.lang.IllegalArgumentException: FormUrlEncoded can only be
specified on HTTP methods with request body (e.g., #POST).
Remove #FormUrlEncoded in your code .
public interface APIService {
#GET("Event")
Call<ApiResponseModel> viewEvent();
}
If you replace #POST request with #GET, change also parameters and remove #FormUrlEncoded:
#GET(/some/request/)
suspend fun getData(
#Query("name") name: String?,
#Query("age") age: Int?
): SomeResponse

Retrofit 2: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

I am use Retrofit 2. I use test JSON on http://ip.jsontest.com/.It is very simple JSON. Why I am take this error?
In real project i have this ERROR too, but i think, it is because I have very big JSON. And I thy use test JSON. Need HELP))
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
This is JSON
{
"ip": "54.196.188.78"
}
My Interface
public interface UmoriliApi {
#GET(".")
Call<List<Test>> getData();
}
My Test class
public class Test {
#SerializedName("ip")
#Expose
private String ip;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
My API class
public class App extends Application {
private static UmoriliApi umoriliApi;
private Retrofit retrofit;
#Override
public void onCreate() {
super.onCreate();
retrofit = new Retrofit.Builder()
.baseUrl("http://ip.jsontest.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
umoriliApi = retrofit.create(UmoriliApi.class);
}
public static UmoriliApi getApi() {
return umoriliApi;
}
}
My MainActivity class
public class MainActivity extends AppCompatActivity {
private static final String TAG = "TAG";
List<Test> posts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
posts = new ArrayList<>();
App.getApi().getData().enqueue(new Callback<List<Test>>() {
#Override
public void onResponse(Call<List<Test>> call, Response<List<Test>> response) {
posts.addAll(response.body());
Log.d(TAG, "onResponse: "+posts.size());
}
#Override
public void onFailure(Call<List<Test>> call, Throwable t) {
Log.d(TAG, "onFailure: ");
}
});
}
}
Basically you are expecting and Array but you received a JSON Object.
As Akash said in the comment:
Call<List<Test>> getData();
List<Test> is what you write when you expect and Array. You need to write Call<Test> for an object Test
You will also have to change the callback.

Categories