I'm making requets to Google Books API with Retrofit2 and use GSON converter, but have an error
ErrorExpected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path
$
API URL to JSON
This is my
BookService.java
public class BookService{
private static final String BASE_URL = "https://www.googleapis.com/";
private BookApiService mApiService;
private BookCallback mListener;
public BookService(BookCallback listener){
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();
mApiService = retrofit.create(BookApiService.class);
mListener = listener;
}
public void getBooks(String query){
final ApiResponse apiResponse = new ApiResponse();
Call<List<Book>> call = mApiService.getBooks(query);
call.enqueue(new Callback<List<Book>>() {
#Override
public void onResponse(Call<List<Book>> call, Response<List<Book>> response) {
apiResponse.setBooks(response.body());
mListener.notifyDataReceived(apiResponse);
}
#Override
public void onFailure(Call<List<Book>> call, Throwable t) {
apiResponse.setError(t);
mListener.notifyDataReceived(apiResponse);
}
});
}
public interface BookCallback{
void notifyDataReceived(ApiResponse apiResponse);
}
}
and my Interface
BookApiService.java
public interface BookApiService {
#GET("/books/v1/volumes")
Call<List<Book>> getBooks(#Query("q") String query);
}
Then making request from MainActivity.java that implements BookCallback
public class MainActivity extends AppCompatActivity implements BookService.BookCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main)
new BookService(this).getBooks("android");
}
}
Your response is not an array. its an object with array in it.
For fix this you must create other response class like BookResponse
BookResponse.java
public class BookResponse{
#SerializedName("kind")
private Sting kind;
#SerializedName("totalItems")
private Int totalItems;
#SerializedName("items")
private List<Book> items;
}
And change your interface like this
public interface BookApiService {
#GET("/books/v1/volumes")
Call<BookResponse> getBooks(#Query("q") String query);
}
The response you are having is not an array of Books, but it is an object, which perhaps is a response that has an array with books. So what the error says: Your JsonResponse is not starting with [] but is starting with {}
Related
I was testing the API in the Retrofit for first time, I was putting the code that they were showing but I don't know why my code doesn't work
it just shows that I am receiving the null value;
I was learning to implement the Retrofit library in my android app . so i was following some tutorial and i was following the code but don't know why i am getting this error
i tried to solve the error by changing the url but the response is the same it shows the null.
Please help me out
public class Todo {
private int userId;
private int id;
private String todo;
private boolean completed;
public int getUserId() {
return userId;
}
public int getId() {
return id;
}
public String getTitle() {
return todo;
}
public boolean isCompleted() {
return completed;
}
}
public interface ApiInterface {
#GET("/todo/{id}")
Call<Todo> getTodo(#Path("id") int id);
}
public class ApiClient {
private static final String BASE_URL ="https://dummyjson.com";
private static Retrofit retrofit= null;
public static Retrofit getclient(){
if(retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
public class MainActivity extends AppCompatActivity {
TextView hello;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
hello= findViewById(R.id.textview);
ApiInterface apiInterface = ApiClient.getclient().create(ApiInterface.class);
Call<Todo> call =apiInterface.getTodo(1);
call.enqueue(new Callback<Todo>() {
#Override
public void onResponse(Call<Todo> call, Response<Todo> response) {
Log.e("info","onResponse"+response.body());
#Override
public void onFailure(Call<Todo> call, Throwable t) {
Log.e("info","onFailure" + t.getLocalizedMessage());
}
});
}
}
You are missing the plural 's' in todo. Change your path to #GET("/todos/{id}")
I'm making a response to this Google Books API URL
I have an Retrofit request:'
public interface BookApiService {
#GET("/books/v1/volumes")
Call<Books> getBooks(#Query("q") String query);
}
And have an Entity classes
Books.java
public class Books {
#SerializedName("items")
#Expose
private List<Book> items;
public List<Book> getItems() {
return items;
}
}
and Book.java
public class Book {
#SerializedName("title")
#Expose
private String mTitle;
public String getTitle() {
return mTitle;
}
}
Request is working correct, i mean no errors with connection, and retrofit returns onResponse, not onFailure.
But my String "title" is null. How can i get this. Please check JSON response by link above.
UPDATE:
Retrofit call:
public class BookService{
private static final String BASE_URL = "https://www.googleapis.com/";
private BookApiService mApiService;
private BookCallback mListener;
public BookService(BookCallback listener){
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();
mApiService = retrofit.create(BookApiService.class);
mListener = listener;
}
public void getBooks(String query){
Call<Books> call = mApiService.getBooks(query);
call.enqueue(new Callback<Books>() {
#Override
public void onResponse(Call<Books> call, Response<Books> response) {
mListener.notifyDataReceived(response.body());
}
#Override
public void onFailure(Call<Books> call, Throwable t) {
mListener.notifyErrorReceived(t);
}
});
}
public interface BookCallback{
void notifyDataReceived(Books books);
void notifyErrorReceived(Throwable error);
}
BookCallback listener is my MainActivity:
When i check Logs, there is
"SUCCESS" "TITLE 1 null"
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.addItemDecoration(new DividerItemDecoration(mRecyclerView.getContext(), DividerItemDecoration.VERTICAL));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
new BookService(this).getBooks("android");
}
#Override
public void notifyDataReceived(Books books) {
if(books.getItems() != null) {
List<Book> items = books.getItems();
Log.d(TAG, "Success");
Log.d(TAG, "TITLE 1" + items.get(0).getTitle());
setupAdapter(items);
}
}
You can request the JSON directly in your browser here. Looking closely at this data, you will see that items[0] doesn't have a property title. Instead, there is a property volumeInfo that contains title. Your Retrofit entity class needs to correctly navigate to that property in order to get the data you need.
I'm trying to make a call to youtube data API to retrieve the subscriber count of a particular channel. But I don't know how to implement the API interface and define the endpoints so I pasted my whole remaining URL in the #GET method. But my app is crashing when started.
My complete URL is: https://www.googleapis.com/youtube/v3/channels?part=statistics&id=+UC-lHJZR3Gqxm24_Vd_AJ5Yw&key=AIzaSyAyON6YdgkFrtNHrGGs3IFS4groadJhhts
Here is my interface :
public interface ApiInterface
{
#GET("/channels?part=statistics&id=+UC-lHJZR3Gqxm24_Vd_AJ5Yw&key=AIzaSyAyON6YdgkFrtNHrGGs3IFS4groadJhhts")
Call<Mainjson> getMainJson();
}
Main Activity :
public class MainActivity extends AppCompatActivity
{
private Statistics statistics;
private String subscribers;
private TextView subscribersPreview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
subscribersPreview=(TextView) findViewById(R.id.textView3);
ApiInterface service = ApiClient.getClient().create(ApiInterface.class);
Call<Mainjson> call = service.getMainJson();
call.enqueue(new Callback<Mainjson>() {
#Override
public void onResponse(Call<Mainjson> call, Response<Mainjson> response) {
List<Items> items = response.body().getItems();
statistics=items.get(0).getStatistics();
subscribers=statistics.getSubscriberCount();
subscribersPreview.setText(subscribers);
}
#Override
public void onFailure(Call<Mainjson> call, Throwable t) {
Toast.makeText(MainActivity.this,"Failed to retrieve data",Toast.LENGTH_SHORT).show();
}
});
}
}
The retrofit instance :
public class ApiClient {
public static final String BASE_URL = "https://www.googleapis.com/youtube/v3";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
problem with BASE_URL.
BASE_URL should be end with / and remove / on start of interface method.
public static final String BASE_URL = "https://www.googleapis.com/youtube/v3/";
#GET("channels?part=statistics&id=+UC-lHJZR3Gqxm24_Vd_AJ5Yw&key=AIzaSyAyON6YdgkFrtNHrGGs3IFS4groadJhhts")
Call<Mainjson> getMainJson();
Easiest and fast way to implement use Android Fast networking use this:-
Android Fast networking
. Get stuck with a basic scenario of loading the data in oncreate of an activity. So I am trying to load the data as soon as i open my activity but when i change the screen orientation it gets called again.
below is my rest client for retrofit
public class MyRestApiClient {
private static Retrofit retrofit = null;
public static Retrofit getClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).connectTimeout(30,TimeUnit.SECONDS).readTimeout(30,TimeUnit.SECONDS).build();
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:8080/rest/")
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit;
}
}
below is my resturl interface for loading the data
public interface MyRestUrlInterface {
#GET("user/{user_id}")
Call<Object> getData(#Path("user_id") String user_id);
}
below is my viewmodel class:
public class MyViewModelObserver extends ViewModel {
private MutableLiveData<Object> httpCallBackObserver;
public MutableLiveData<Object> getHttpCallBackObserver() {
if (httpCallBackObserver == null) {
httpCallBackObserver = new MutableLiveData<Object>();
}
return httpCallBackObserver;
}
}
below is my Activity code :
public class MyActivity extends AppCompatActivity {
private static final String TAG = "MyActivity" ;
MyRestUrlInterface restUrlInterface;
public MyViewModelObserver myViewModelObserver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
restUrlInterface = MyRestApiClient.getClient().create(MyRestUrlInterface.class);
myViewModelObserver = ViewModelProviders.of(this).get(MyViewModelObserver.class);
myViewModelObserver.getHttpCallBackObserver().observe(this, getData());
//load data via http
Call<Object> call = restUrlInterface.getData("123");
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
myViewModelObserver.getHttpCallBackObserver().setValue(response.body());
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
}
});
}
private Observer<Object> getData(){
return new Observer<Object>() {
#Override
public void onChanged(#Nullable final Object responseString) {
Log.d(TAG,"***** Loaded Data --- "+responseString);
}
};
}
}
How to use view model so that it wont make http call again in screen orientation change
suggested answer:
public class MyViewModelObserver extends ViewModel {
private MutableLiveData<Object> httpCallBackObserver;
public MutableLiveData<Object> getHttpCallBackObserver() {
if (httpCallBackObserver == null) {
httpCallBackObserver = new MutableLiveData<Object>();
loadData();
}
return httpCallBackObserver;
}
private void loadData(){
Call<Object> call = restUrlInterface.getData("123");
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
myViewModelObserver.getHttpCallBackObserver().setValue(response.body());
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
}
});
}
}
I'm trying to get data from web api but something is wrong and I can't figure it out.
My JSON looks like this
{
"partners":
[
"partner1",
"partner2",
"partner3",
... ,
"partner150"
]
}
I have Table partners (ActiveAndroid) in which I would like to save all partners from api.
#Table(name = "Partners")
public class Partners extends Model {
#Column(name = "Name")
String name;
public Partners() {}
public Partners(String name) {
this.name = name;
}
}
Here is my Pojo model class:
public class Partners extends ArrayList<String> {
#SerializedName("partners")
#Expose
public List<String> partners = new ArrayList<String>();
public List<String> getPartners() {
return partners;
}
public void setName(List<String> partners) {
this.partners = partners;
}
}
This is my interface
public interface APIService {
#GET("Partners")
Call<Partners> getPartners();
}
This is my APIHelper class
public class APIHelper {
public static final String PARTNERS_URL = "https://part-of-link.com/partners.json/";
public static APIService apiService;
public static APIService getApiService() {
if (apiService == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(PARTNERS_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
apiService = retrofit.create(APIService.class);
}
return apiService;
}
}
And this is Fragment where I have an Button on which I would like to implement onClick method to get data from API and save it into Partners table.
public class DownloadMain extends Fragment implements Callback<Partners> {
private Button dloadPartners;
private Call<Partners> callPartners;
public static APIService apiService;
public DownloadMain() {}
public DownloadMain newInstance() { return new DownloadMain(); }
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.download_main, container, false);
dloadPartners = (Button) view.findViewById(R.id.downloadPartners);
dloadPartners.setOnClickListener(btnListener);
callPartners = APIHelper.getApiService().getPartners();
callPartners.enqueue(this);
return view;
}
Button.OnClickListener btnListener = (new View.OnClickListener() {
#Override
public void onClick(View v) {
APIHelper helper = new APIHelper();
apiService = helper.getApiService();
Call<Partners> call = apiService.getPartners();
call.enqueue(new Callback<Partners>() {
#Override
public void onResponse(Call<Partners> call, Response<Partners> response) {
APIHelper helper = new APIHelper();
helper.getApiService();
Call<Partners> call = apiService.getPartners();
call.enqueue(new Callback<Partners>() {
#Override
public void onResponse(Call<Partners> call, Response<Partners> response) {
List<String> partners = response.body().getPartners();
Log.d(TAG, "Number of partners received: " + partners.size());
/*if (response.body() != null) {
ArrayList<String> partnersList;
partnersList = response.body();
Log.d("DOWNLOAD", String.valueOf(partnersList));
}*/
}
#Override
public void onFailure(Call<Partners> call, Throwable t) {
Toast.makeText(getActivity(), "FAIL!!!", Toast.LENGTH_SHORT).show();
}
});
}
});
#Override
public void onResponse(Call<Partners> call, Response<Partners> response) {
}
#Override
public void onFailure(Call<Partners> call, Throwable t) {
}
}
And here is my problem. Everything works and there is no errors but when I click on button nothing happens.
So I presume I've made some mistake but I can't figure it out (First time doing with retrofit).
What I want on button click is to get all partners from web and save it into my Partners table.
Question: Could somebody guide me and tell me what is wrong and help me to fix this?
EDIT:
Button.OnClickListener btnListener = (new View.OnClickListener() {
#Override
public void onClick(View v) {
APIHelper helper = new APIHelper();
apiService = helper.getApiService();
Call<ArrayList<String>> call = null;
call.enqueue(new Callback<ArrayList<String>>() {
#Override
public void onResponse(Call<ArrayList<String>> call, Response<ArrayList<String>> response) {
ArrayList<String> partners = response.body();
Log.d(TAG, "Number of partners received: " + partners.size());
}
#Override
public void onFailure(Call<ArrayList<String>> call, Throwable t) {
Toast.makeText(getActivity(), "FAIL!!!", Toast.LENGTH_SHORT).show();
}
});
}
});
Error logs:
Error:(68, 60) error: is not abstract and does not
override abstract method
onResponse(Call>,Response>) in
Callback
Error:(72, 53) error: cannot find symbol method body()
Error:(69, 17) error: method does not override or implement a method
from a supertype
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
Compilation failed; see the compiler error output for details.
your problem is here in the onClick()
helper.getApiService();
you did not assign the service into the variable
change it to this:
apiService = helper.getApiService();
EDIT:
based on new updates i think you dont need to use a POJO
just use the following in your Call
Call<ArrayList<String>>
and to get response just use response.body() without .partners() .
try to replace it everywhere in your code and check
Based on your JSON response, your POJO class should be:
public class Partners {
private ArrayList<String> partners = new ArrayList<>(0);
// Get; Set methods //
}
Try putting this in your APIHelper class to get more information about what you are sending
public static APIService apiService;
//this http loggin interceptor allows you debug requests and responses
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// set your desired log level
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// add your other interceptors …
// add logging as last interceptor
httpClient.addInterceptor(logging); // <-- this is the important line!
public static APIService getApiService() {
if (apiService == null) {
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
}
return apiService;
}
Maybe you are sending information in a wrong way...
And remember that in server side you must decode the information that receive (with json_decode).