Parse nested json using retrofit in Android - android

I am getting a Null Pointer Exception. I want to retrieve a random quote from this Quotes Api - https://quotes.rest/ to display in my android application. Where am I going wrong?
(I apologise if I had not asked the question properly or violated any rules by posting a question. This is my first time asking a question)
I have created necessary POJOs and tried retrieving the quote in the MainActivity.
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<Example> call = service.getRandomQuote();
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
String quote = response.body().getContents().getQuotes().get(0).getQuote();
Log.d(TAG, "onResponse: ************" + quote);
}
GetDataService
public interface GetDataService {
#GET("/quote/random")
Call<Example> getRandomQuote();
}
POJO- Quote
public class Quote {
#SerializedName("quote")
private String quote;
public Quote(String quote) {
this.quote = quote;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
}
POJO- Contents
public class Contents {
#SerializedName("quotes")
#Expose
private List<Quote> quotes;
public List<Quote> getQuotes() {
return quotes;
}
public void setQuotes(List<Quote> quotes) {
this.quotes = quotes;
}
}
POJO- Example
public class Example {
#SerializedName("contents")
#Expose
private Contents contents;
public Contents getContents() {
return contents;
}
public void setContents(Contents contents) {
this.contents = contents;
}
}
RetrofitClientInstance class
public class RetrofitClientInstance {
private static Retrofit retrofit = null;
private static final String BASE_URL = "https://quotes.rest";
public static Retrofit getRetrofitInstance(){
if(retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
I expect the output to be a random quote from this Quotes Api (https://quotes.rest/) but I am getting a Null Pointer Exception. Following is the error-
java.lang.NullPointerException: Attempt to invoke virtual method 'com.platinumstudio.contactsdemo7.network.Contents com.platinumstudio.contactsdemo7.Example.getContents()' on a null object reference
at com.platinumstudio.contactsdemo7.MainActivity$3.onResponse(MainActivity.java)

The API that you're using here is this. As the API documentation says, an API key is required to fetch data. Since you didn't add an API key in your codes, it's should return an unsuccessful response with the HTTP Status Code of 401.
And it's always the best practice to check if the response is successful before proceeding to execute operations on received data to avoid crash issues in the app. Always keep this code snippet when you're fetching data from an API:
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
if(response.isSuccessful && response.body() != null){
//Get contents & quote
}
else {
//Check response code & show the relevant error message through Toast or Dialog
}
}
#Override
public void onFailure(Call call, Throwable t) {
//Show a failed message to the user
}
}

I believe that you need to add Api key to your request, otherwise you'll not be allowed to access the data. Hence, your response's body is null, which causes the crash. As it says on the website:
For using the private end points and subscribing to the API please visit https://theysaidso.com/api.

https://theysaidso.com/api/
From the API website you will find the link to access the ramdom quote
here is the link:
http://quotes.rest/quote/random.json?api_key=
without the key you will get Unauthorized access

This API requires Authorization key to access the data. Hence, modify your interface GetDataService like below :
public interface GetDataService {
#GET("/quote/random/")
Call<Example> getRandomQuote(#HeaderMap Map<String, String> headerMap);
}
//HeaderMap will have authorization key and its value as <Key,Value> pair.

Related

How to make multiple requests using retrofit2 and rxjava2 on android?

In my android app, I want to make multiple http requests using retrofit and rxjava to fetch json data. The number of request depends on the user's preferences (1 up to 40). Each request is independent and returns same type. So, i tried to apply the way that is recommended in this question (How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android) which uses the zip function of rx-java. But i couldn't find a way to get and combine the results of each request. Response type that i used for single request in retrofit was Response<List<NewsItem>> where NewsItem is my custom object. (the response is json array actually but in a single request retrofit automatically handles it and converts it into list of my custom object) What i tried so far is below:
My API Interface
public interface API {
String BASE_URL = "xxx/";
#GET("news/{source}")
Observable<List<NewsItem>> getNews(#Path("source") String source);
}
Viewmodel class to fetch data
public class NewsVM extends AndroidViewModel {
public NewsVM(Application application){
super(application);
}
private MutableLiveData<List<NewsItem>> newsLiveData;
public LiveData<List<NewsItem>> getNewsLiveData(ArrayList<String> mySourceList) {
newsLiveData = new MutableLiveData<>();
loadNews(mySourceList);
return newsLiveData;
}
private void loadNews(ArrayList<String> mySourceList) {
Gson gson = new GsonBuilder().setLenient().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
API api = retrofit.create(API.class);
//Gathering the requests into list of observables
List<Observable<?>> requests = new ArrayList<>();
for(String source: mySourceList){
requests.add(api.getNews(source));
}
// Zip all requests
Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
#Override
public List<NewsItem> apply(Object[] objects) throws Exception {
// I am not sure about the parameters and return type in here, probably wrong
return new ArrayList<>();
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(
new Consumer<List<NewsItem>>() {
#Override
public void accept(List<NewsItem> newsList) throws Exception {
Log.d("ONRESPONSE",newsList.toString());
newsLiveData.setValue(newsList);
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
Log.d("ONFAILURE", e.getMessage());
}
}
).dispose();
}
}
It doesn't give error but doesn't give response also since I couldn't handle the response. Can anybody help me in combining the results of the each request? I've searched all the questions but can't find an example like this.
try to use Observable.from(Iterable<? extends T> iterable) (Observable.fromArray() in rx-java2) instead of zip
So you'll have something like:
Observable.from(mySourceList)
.flatMap(new Func1<String, Observable<List<NewsItem>>>() {
#Override
public Observable<List<NewsItem>> call(String source) {
return api.getNews(source);
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.toList() // This will give you List<List<NewsItem>>
.map(new Func1<List<List<NewsItem>>, List<NewsItem>>() {
#Override
public List<NewsItem> call(List<List<NewsItem>> listOfList) {
//Merged list of lists to single list using Guava Library
List<NewsItem> list = Lists.newArrayList(Iterables.concat(listOfList));
return list;
}
})
.subscribe(new Subscriber<List<NewsItem>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(List<NewsItem> newsList) {
//Attached the final newslist to livedata
newsLiveData.setValue(newsList);
}
});
EDITED Updated the method
Object[] objects is the array of items returned by the requests. Assuming that each of your request returns a List<NewsItem> and you wanted to combine all NewsItem into a single List<NewsItem>, I think you can do something along the lines of:
private void loadNews(ArrayList<String> mySourceList) {
...
// Zip all requests
Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
#Override
public List<NewsItem> apply(Object[] objects) throws Exception {
List<NewsItem> combinedNewsItems = new ArrayList<>();
for (Object response : objects) {
combinedNewsItems.addAll((List<NewsItem>) response);
}
return combinedNewsItems;
}
})
.subscribeOn(Schedulers.io())
...
}
Be aware of the type casting.
If the type of data that you are getting is same List and the requests are multiple then you can use Recursion Method to make 1 to n requests and add data List on every success.

How to pass Parameter In retrofit

Hear is my API:
http://v2sgroups.in/Erp_V2s_Groups/AndroidPanel/OTPVerification/099567.
I try to call Api through retrofit framework , how can I pass parameter in retrofit like above link. 099657 is the passing parameter.
#GET("/AndroidPanel/OTPVerification/")
void otp(#Field("otp") String otp,
Callback<OTP> callback);
how to pass 099567 in using interface?
Its a path, you can do:
#GET("/AndroidPanel/OTPVerification/{otp}")
void otp(#Path("otp") String otp,
Callback<OTP> callback);
You can pass parameter by #QueryMap
Retrofit uses annotations to translate defined keys and values into appropriate format. Using the #Query("key") String value annotation will add a query parameter with name key and the respective string value to the request url .
public interface API{
#POST("media-details")
retrofit2.Call<MediaDetails>getMediaList(#QueryMap Map<String, String> param);
}
private void getData() {
Map<String, String> data = new HashMap<>();
data.put("id", "12345");
data.put("end_cursor", "00000");
Call<MediaDetails> mediaDetails = ServiceAPI.getService().getMediaList(data);
mediaDetails.enqueue(new Callback<MediaDetails>() {
#Override
public void onResponse(Call<MediaDetails> call, Response<MediaDetails> response) {
}
#Override
public void onFailure(Call<MediaDetails> call, Throwable t) {
System.out.println("Failed");
}
});
}

Retrofit in android?

I'm new to Retrofit. How can I send param and get Json from bellow url ?
http://xxx:8087/courier.svc/login?username=jim&password=123456
I need to a link for tutorial .
This code is in my MainActivity :
private void loadJSON(String username, String password) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.8.11:8087/sample.svc/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface_Login request = retrofit.create(RequestInterface_Login.class);
Call<JSONResponseLogin> call = request.getJSON(username, password);
call.enqueue(new Callback<JSONResponseLogin>() {
#Override
public void onResponse(Call<JSONResponseLogin> call, Response<JSONResponseLogin> response) {
JSONResponseLogin jsonResponse = response.body();
data = new ArrayList<>(Arrays.asList(jsonResponse.getLogin()));
}
#Override
public void onFailure(Call<JSONResponseLogin> call, Throwable t) {
Log.d("Error", t.getMessage());
}
});
}
My ModelLogin :
public class ModelLogin {
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
My RequestInterface_Login :
public interface RequestInterface_Login {
#GET("/login/{username}/{password}")
Call<JSONResponseLogin> getJSON(#Path("username") String username, #Path("password") String password);
}
My JSONResponseLogin :
public class JSONResponseLogin {
private ModelLogin[] login;
public ModelLogin[] getLogin() {
return login;
}
}
But get me NullPointerException!
I get json from service same bellow :
{"Key":null,"Response":1}
Before you get call the retrofit you can just print the URL and then you can load URL in browser and see what response is coming you can add log by bellow line before call.enqueue(new Callback<JSONResponseLogin>()
Log.e(TAG, "API URL: " + call.request().url());
Check your response
And let me know I will help you ... coz i am using retrofit in my 3 projects
Do like this ...in interface
#GET("/courier.svc/login?)
Call<JSONResponseLogin> getJSON(#Query("username") String username,
#Query("password") String password);
and remove it from base .baseUrl("http://192.168.8.11:8087")
I am writing this post on 'Retrofit in android' using our latest Retrofit library, This plugin can be be used to Integrate Retrofit to your project.
Before using reftrofit, we just need to follow some steps to add QAssist Plugins in Android studio which will reduce your efforts of Webservices integration.
1.Add (QAssist - Android Studio Plugin) Android plugin in your Android studio. ( https://github.com/sakkeerhussain/QAssist ).:-
2.When we done with the plugin then we need to restart our Android studio.
3.Check your Android studio Menubar You can find "QAssist".
4.Click on that and Start with Retrofit Integrate.
5.Now we need :
- BaseUrl of Web Api's
- All Api's en points.
- Sample request and Response of All request.
- After completing these steps your reftrofit(QAssist/retrofit package is genrated inside Project).
6.You can find A Class and One Interface inside that Package.
7.The interface contain all the endpoints detail and Class will cotain the Retrofit connection details.
8.We just need to add a small code to access and Api and Parse that Api' response to Data model.
9.Use this sample function to call Api.
private void demoRetroFitCall() {
APIService apiService = RetrofitManager.getInstance(this);
//APIService apiService = RetroFitApiClient.getClient().create(APIService.class);
Call<ModelData> call = apiService.TestGet();
call.enqueue(new Callback<ModelData>() {
#Override
public void onResponse(Call<ModelData> call, Response<ModelData> response) {
Log.d("TAG", "Data recived in response.body");
}
#Override
public void onFailure(Call<ModelData> call, Throwable t) {
Log.e("TAG", t.toString());
}
});
}
56 - voda

Combining API responses in RxJava where one of the APIs returns a list

I'm trying to use RxJava to combine the responses from two API calls. The scenario is the following: The first API call returns a list of User objects. Each User object contain a postcode, but they do not contain a city name. To get the city name a second API is called, with the parameter being the postcode.
Ideally I want to modify the original list of User objects, and add the city name after it has been fetched in the second API call, but I'm stuck at figuring out how to do it.
This seems like a pretty straightforward observable:
Dummy API for users:
public static List<String> getUsers() {
return new ArrayList<String>() {
/**
*
*/
private static final long serialVersionUID = 1L;
{
add("Anakin");
add("Darth");
}
};
}
Dummy API for city:
public static String getCity(String name) {
// dummy ..
return "Mos Espa";
}
Merging together:
public static void main(String[] args) throws InterruptedException {
Observable.from(getUsers()).flatMap(
// we have the user
user -> Observable.just(getCity(user)).map(
// we have the city
city -> user + " from " + city)) // do stuff with user + city
.subscribe(result -> System.out.println(result));
}
Here is my understanding of your question. You have 2 API calls...
public Observable<List<User>> getUsers();
public Observable<String> getCityName(String postalCode);
And you want to set each user's city name based on the call to getCityName. You can convert the List into an observable stream by flatMapping it into an Observable from an Iterable. Here is one way to do that.
Observable<User> usersWithCity = getUsers()
.flatMap(new Func1<List<User>, Observable<User>>() {
#Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}})
.flatMap(new Func1<User, Observable<User>>() {
#Override
public Observable<User> call(User user) {
return getCityName(user.getPostalCode())
.doOnNext(new Action1<String>() {
#Override
public void call(String t) {
user.setCityName(t);
}
})
.map(new Func1<String, User>() {
#Override
public User call(String cityName) {
return user;
}
});
}
});
Note that this makes the assumption that there will only ever be one cityName for each postalCode.

Retrofit to consume APIs

I just started using Retrofit to consume APIs. I am able to update a User's profile successfully but I can't pass in the information to a User Object:
Interface RetrofitService:
public interface RetrofitService {
#FormUrlEncoded
#POST("/profile/update")
public void updateUser(
#Field("user_id") String userId,
#Field("user_token") String userToken,
#Field("first_name") String firstName,
#Field("last_name") String lastName,
Callback<JSONObject> callback);
}
Activity ProfileUpdate:
updateProfile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Build RestAdapter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://xxx.xxx.xxx.xxx/")
.build();
//Create API service
RetrofitService service = restAdapter.create(RetrofitService.class);
//Invoke method
service.updateUser(
user.getUserId(),
user.getUserToken(),
mFirstName.getText().toString(),
mLastName.getText().toString(),
new Callback<JSONObject>() {
#Override
// user remains null!
public void success(JSONObject jsonObject, Response response) {
Toast.makeText(ProfileMe.this, "Profile Updated", Toast.LENGTH_SHORT).show();
Log.d("succes", "Success!");
}
#Override
public void failure(RetrofitError error) {
Log.d("failure", "Failed");
}
});
}
});
The above code works and the user's profile is updated. However the JSONObject in the onSuccess remains null.
I've been using Gson until now and I would do something like this:
//update user with new information
User user = gson.fromJson(responseData,User.class);
How do I do this with Retrofit?
EDIT
Response from the server returns a JSONObject. I changed the code above to satisfy those requirements but I am still returned a null JSONObject
Change to this:
public interface RetrofitService {
#FormUrlEncoded
#POST("/profile/update")
public void updateUser(
#Field("user_id") String userId,
#Field("user_token") String userToken,
#Field("first_name") String firstName,
#Field("last_name") String lastName,
Callback<User> callback);
}
You need to set in the callback what you are expecting to receive in the response. If the WS returns a User object you need to set that in the callback
Make sure that your rest has a Object as return (Json formatter).
You need change your code to this:
public interface RetrofitService {
#FormUrlEncoded
#POST("/profile/update")
public void updateUser(
#Field("user_id") String userId,
#Field("user_token") String userToken,
#Field("first_name") String firstName,
#Field("last_name") String lastName,
Callback<User> callback);
}
Retrofit uses Gson by default to convert HTTP bodies to and from JSON.
If you want to specify behavior that is different from Gson's defaults
(e.g. naming policies, date formats, custom types), provide a new Gson
instance with your desired behavior when building a RestAdapter. Refer
to the Gson documentation for more details on customization.
Edited
If your server returns something like this:
{
"user_id": "21q3123"
"user_token":"asd2334rter"
"first_name" : "User Test"
"last_name" : "user last name"
}
On your Android client you need to have something like this also
public class User {
String user_id;
String user_token;
String first_name;
String last_name;
//Getter and Setters
}
Retrofit by default use Gson to parser all requests.
See here a good example of Paser.com and Retrofit:
Android-Retrofit-Example

Categories

Resources