How do I parse nested objects with Retrofit 2? - java

I'm consuming a newsapi whose JSON response is similar to this;
{
"status": "ok",
"articles": [
{
"source": {
"id": "bbc-news",
"name": "BBC News"
},
"author": "BBC News",
"title": "Jubilation greets end of Mugabe era",
"description": "Zimbabweans celebrate late into the night after Robert Mugabe resigns, ending 37-year rule.",
"url": "http://www.bbc.co.uk/news/world-africa-42072673",
"urlToImage": "https://ichef.bbci.co.uk/images/ic/1024x576/p05nt3bn.jpg",
"publishedAt": "2017-11-22T02:46:09Z"
},
{
"source": {
"id": "bbc-news",
"name": "BBC News"
},
"author": "BBC News",
"title": "Dramatic moment N Korea soldier defects",
"description": "He raced across the border on foot, closely pursued by North Korean troops who shot at him several times.",
"url": "http://www.bbc.co.uk/news/world-asia-42075986",
"urlToImage": "https://ichef.bbci.co.uk/news/1024/cpsprodpb/F519/production/_98854726_p05ntph4.jpg",
"publishedAt": "2017-11-22T04:45:14Z"
},
{
....
}
]
}
I'm trying to consume that response using Retrofit 2 with GSON.
My POJO Classes are these;
NewsList.java
public class NewsList {
public Articles[] articles;
private String status;
#Override
public String toString() {
return "ClassPojo [articles = " + articles + ", status = " + status + "]";
}
// getters and setters
}
Articles.java
public class Articles {
private String publishedAt;
private String author;
private String urlToImage;
private String title;
private Source source;
private String description;
private String url;
// getters and setters
}
Source.java
public class Source {
private String id;
private String name;
// getters and setters
}
My Retrofit Client looks like this;
public interface NewsroomAPI {
String BASE_URL = "https://newsapi.org/v2/";
#GET("top-headlines")
Call<NewsList> loadNews(#Query("sources") String source);
}
In my MainActivity.java I make calls to the Retrofit client like this;
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().addInterceptor(new Interceptor() {
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder().header("Authorization",
getString(R.string.newsroom_api_key));
Request newRequest = builder.build();
return chain.proceed(newRequest);
}
}).build();
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(NewsroomAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client(okHttpClient).build();
NewsroomAPI getNewsAPI = retrofit.create(NewsroomAPI.class);
Call<NewsList> call = getNewsAPI.loadNews("bbc-news");
call.enqueue(new Callback<NewsList>() {
#Override public void onResponse(Call<NewsList> call, Response<NewsList> response) {
if (response.isSuccessful()) {
NewsList newslist = response.body();
Log.w(TAG, "Articles result: " + newslist);
} else {
Toast.makeText(getContext(), "Some error occurred while fetching results!",
Toast.LENGTH_SHORT).show();
}
}
#Override public void onFailure(Call<NewsList> call, Throwable t) {
Log.w(TAG, "Failed! ", t);
}
});
The problem comes when I run the activity and log the results.
I would expect a log with the status and the returned articles. However, the status is returned successfully, but the articles object is null. The output looks like this;
W/GlobalNewsFragment: Articles result: ClassPojo [articles = null, status = ok]
The problem seems to be coming from the way Retrofit2 is deserializing the returned JSON object. Is there anything I'm doing wrong?
These are the dependencies in my build.gradle file
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile "com.android.support:recyclerview-v7:26.1.0"
compile "com.squareup.okhttp3:okhttp:3.8.0"

Your POJO class should be like below.
public class Article {
#SerializedName("source")
#Expose
private Source source;
#SerializedName("author")
#Expose
private String author;
#SerializedName("title")
#Expose
private String title;
#SerializedName("description")
#Expose
private String description;
#SerializedName("url")
#Expose
private String url;
#SerializedName("urlToImage")
#Expose
private String urlToImage;
#SerializedName("publishedAt")
#Expose
private String publishedAt;
// your getter setter methods
}
Your NewsList POJO like below.
public class NewsList {
#SerializedName("status")
#Expose
private String status;
#SerializedName("articles")
#Expose
private List<Article> articles = null;
// getter setter
}
And your source POJO like below.
public class Source {
#SerializedName("id")
#Expose
private String id;
#SerializedName("name")
#Expose
private String name;
// getters setters
}

AFAIK Gson deserialize JsonArray into List. Try this:
public class NewsList {
public List<Articles> articles;
private String status;
//some code
}

Related

Nested object with Gson returns null when class is imported

I'm rusty on my Java so I was wondering if anyone could help. I have a Json that comes in from a rest API and I can't seem to get the nested objects to get read, but the fields are there so I'm at a loss.
Here is what my json string looks like (result from String result = response.getEntity(String.class); in wrapper.java)
result = "{"FIRST_NAME":"Test First","LAST_NAME":"Test Last","testAttr":[{"MOTHER_NAME":"Test Mother 1","FATHER_NAME":"Test Father 1"}, {"MOTHER_NAME":"Test Mother 2","FATHER_NAME":"Test Father 2"}]}"
(to better read it)
result = {
"FIRST_NAME": "Test First",
"LAST_NAME": "Test Last",
"testAttr": {
"MOTHER_NAME":"Test Mother 1",
"FATHER_NAME":"Test Father 1"
},
{
"MOTHER_NAME":"Test Mother 2",
"FATHER_NAME":"Test Father 2"
}
}
Using the code below, I was able to get the firstName and lastName without a problem, but I wasn't able to get the nested objects unless I explicitly had them put inside the same with (with the #Serialized, #Exposure). I'm not sure where exactly I've gone wrong since I have no errors with importing :/
Main.java
#GET
#Path("/api/test")
#Produces(MediaType.APPLICATION_JSON)
public Response getAttributrs(#Context HttpServletRequest req) {
ResponseObj responseObj = new ResponseObj();
try {
ResponseObj listOfAttr = wrapper.getAttr();
return Response.ok(listOfAttr).build();
} catch (Exception e) {
wrapper.manageError(e, responseObj) ;
return Response.status(500).entity(responseObj).build();
}
}
Wrapper.java
public ResponseObj getAttr() throws Exception {
Client client = ClientHelper.createClient();
WebResource webResource = client
.resource("https://xxxxxxxxx);
webResource.header("X-METHOD-OVERRIDE", "GET");
webResource.header("content-type", "application/json");
ClientResponse response = webResource.type("application/json").get(ClientResponse.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
String result = response.getEntity(String.class);
ResponseObj responseObj = new Gson().fromJson(result, ResponseObj.class);
return responseObj;
}
ResponseObj.java
#SerializedName("testAttr")
#Expose
private List<AttributesClass> testAttributes;
// getters/setters
AttributeClass.java
private TestInnerClass testInnerClass;
#SerializedName("FIRST_NAME")
#Expose
private String firstName;
#SerializedName("LAST_NAME")
#Expose
private String lastName;
//getters/setters
TestInnerClass.java
#SerializedName("MOTHER_NAME")
#Expose
private String mothersName;
#SerializedName("FATHER_NAME")
#Expose
private String fathersName;
//getters/setters
Your model classes should be like below
Class 1
public class ResponseObj {
#SerializedName("FIRST_NAME")
private String fIRSTNAME;
#SerializedName("LAST_NAME")
private String lASTNAME;
#SerializedName("testAttr")
private List<AttributeClass> testAttr = null;
// getter and setter
Class2
public class AttributeClass {
#SerializedName("MOTHER_NAME")
private String mOTHERNAME;
#SerializedName("FATHER_NAME")
private String fATHERNAME;
// getter and setter
two classes are sufficient.

Retrofit not parsing in Android

I am having trouble parsing the values and displaying in the recyclerview made the Pojo class's in jsonschema2pojo, but when i run the app, it shows the toast in the enqueue OnFailure(), i tried multiple things but no success any help i think it can be about the expecting jsonArray/jsonObject thing ?
Thank you in advance.
I want to get the values inside the array results[]
Json response given below:
"success": true,
"metadata": {
"sort": "POPULARITY",
"total_products": 20,
"title": "Phones & Tablets",
"results": [
{
"sku": "1",
"name": "Samsung Galaxy S9",
"brand": "Samsung",
"max_saving_percentage": 30,
"price": 53996,
"special_price": 37990,
"image": "https://cdn2.gsmarena.com/vv/bigpic/samsung-galaxy-s9-.jpg",
"rating_average": 5
},
APIReponse pojo class:
public class APIReponse {
#SerializedName("success")
#Expose
private Boolean success;
#SerializedName("metadata")
#Expose
private Metadata metadata;
MetaData pojo class:
public class MetaData {
#SerializedName("sort")
#Expose
private String sort;
#SerializedName("total_products")
#Expose
private Integer totalProducts;
#SerializedName("title")
#Expose
private String title;
#SerializedName("results")
#Expose
private List<Result> results = null;
Result pojo class:
public class Result {
#SerializedName("sku")
#Expose
private String sku;
#SerializedName("name")
#Expose
private String name;
#SerializedName("brand")
#Expose
private String brand;
#SerializedName("max_saving_percentage")
#Expose
private Integer maxSavingPercentage;
#SerializedName("price")
#Expose
private Integer price;
#SerializedName("special_price")
#Expose
private Integer specialPrice;
#SerializedName("image")
#Expose
private String image;
#SerializedName("rating_average")
#Expose
private Integer ratingAverage;
This is retrofit API interface:
#GET("search/phone/page/1/")
Call<List<Result>> getAllPhones();
Retrofit call methods:
Call<List<Result>> call = service.getAllPhones();
call.enqueue(new Callback<List<Result>>() {
#Override
public void onResponse(Call<List<Result>> call, Response<List<Result>> response) {
generatePhonesList(response.body());
}
#Override
public void onFailure(Call<List<Result>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}
private void generatePhonesList(List<Result> phonesList){
recyclerView = findViewById(R.id.recyclerView);
adapter = new PhonesAdapter(phonesList,this);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
You have to use your APIResponse Class as Result from your call
#GET("search/phone/page/1/")
Call<APIResponse> getAllPhones();
and you have to change your onResponse Method:
call.enqueue(new Callback<APIResponse>() {
#Override
public void onResponse(Call<APIResponse> call, Response<APIResponse> response) {
generatePhonesList(response.body().metadata.results);
}
#Override
public void onFailure(Call<APIResponse> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
You need create data class same:
class DataResponse {
val success: String = ""
val metadata: MetadataResponse? = null
class MetadataResponse {
val sort: String = ""
val total_products: Int = 0
val title: String = ""
val results: List<ItemResult> = arrayListOf()
class ItemResult {
val sku: String = ""
val name: String = ""
val brand: String = ""
val max_saving_percentage: Int = 0
val price: Int = 0
val special_price: Int = 0
val image: String = ""
val rating_average: Int = 0
}
}
And:
#GET("search/phone/page/1/")
Call<DataResponse> getAllPhones();
And:
Call<DataResponse> call = service.getAllPhones();
call.enqueue(new Callback<DataResponse>() {
#Override
public void onResponse(Call<DataResponse> call, Response<DataResponse> response) {
val itemResults = response.metadata?.results
}
#Override
public void onFailure(Call<List<Result>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
You should use APIReponse instead of Result class
Like this.
#GET("search/phone/page/1/")
Call<APIReponse> getAllPhones();

How to get data from retrofit when data is like this as given below

When I try to get name while parsing the response from the server, I am getting null. Can u please help me to get data i.e, name, email and keyskills.name?
The JSON response is here.
{
"freelancer": {
"id": 3,
"name": "trinadh",
"title": "web developer",
"email": "trinadh_freelancer#gmail.com",
"gender": "Male",
"dob": "2018-09-27",
"website": "www.trinadh_freelancer.com",
"country": "India",
"state": "Karnataka",
"city": "Bangalore",
"user_id": 52,
"user_role": "freelancer",
"registered": null,
"agreement": true,
"address": "hsr layout",
"qualification": "b.tech",
"total_experience": "2",
"prefered_location": "",
"category": "Web Development",
"pancard_number": "ajhfvbqjhe",
"passport_number": "hbhfjdhbjfh",
"country_code": null,
"contact_number": "8765456721",
"currency_type": "INR",
"rate": "678.0",
"rate_card_type": "per_hour",
"negotiable": true,
"taxes": "Taxes Excluded",
"key_skills": {
"name": "ruby",
"relevant_experience": "2"
},
"other_skills": {
"name": "animation",
"relevant_experience": "3"
},
"confirmed_at": "24-Sep-2018",
"free_trail_days_left": 83,
"renewal_date": "24-Mar-2019",
"image": "<img src=\"\" />"
}
}
Here is my pojo class
public class FreeLancer {
private List<FreeLancerProfile> freelancer;
// Constructors, getters and setters are removed for convenience
}
Here is my freelancer profile
public class FreeLancerProfile {
private int id;
private String name;
private String title;
private String email;
private String gender;
private String dob;
private String website;
private String country;
private String state;
private String city;
private int user_id;
private String user_role;
private String registered;
private String agreement;
private String address;
private String qualification;
private String total_experience;
private String prefered_location;
private String category;
private String pancard_number;
private String passport_number;
private String country_code;
private String contact_number;
private String currency_type;
private String rate;
private String rate_card_type;
private String negotiable;
private String taxes;
private List<KeySkill> key_skills;
private List<OtherSkill> other_skills;
private String confirmed_at;
private String free_trail_days_left;
private String renewal_date;
private String image;
// Constructors, getters and setters are removed for convenience
}
My Pojo class for key skills
public class KeySkill {
private int id;
private String name;
private String relevant_experience;
// Constructors, getters and setters are removed for convenience
}
My Interface
public interface FreeLancerMainApi {
#GET("{fullUrl}/profile")
Call<FreeLancerProfile> freeLancerMain(
#Path(value = "fullUrl", encoded = true) String fullUrl,
#Header("Authorization") String token
);
#GET("{fullUrl}/profile")
Call<KeySkill> keySkillsMain(
#Path(value = "fullUrl", encoded = true) String fullUrl,
#Header("Authorization") String token
);
}
My Main Avtivity
String BASE_URL = "http://74.207.233.160/api/v1/freelancers/";
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
final FreeLancerMainApi api = retrofit.create(FreeLancerMainApi.class);
Call<FreeLancerProfile> call = api.freeLancerMain(freelancer_id, token);
call.enqueue(new Callback<FreeLancerProfile>() {
#Override
public void onResponse(Call<FreeLancerProfile> call, Response<FreeLancerProfile> response) {
if (response.code() == 200)
{
name = response.body().getName();
email = response.body().getEmail();
contactNumber = response.body().getContact_number();
Toast.makeText(FreelancerActivity.this, name, Toast.LENGTH_SHORT).show();
}
else {
}
}
#Override
public void onFailure(Call<FreeLancerProfile> call, Throwable t) {
}
});
Call<KeySkill> call1 = api.keySkillsMain(freelancer_id, token);
call1.enqueue(new Callback<KeySkill>() {
#Override
public void onResponse(Call<KeySkill> call, Response<KeySkill> response) {
if (response.code() == 200){
skills = response.body().getName();
}else {
}
}
#Override
public void onFailure(Call<KeySkill> call, Throwable t) {
}
});
freeLancerMainName.setText(name);
freeLancerMainEmail.setText(email);
freeLancerContactNumber.setText(contactNumber);
freeLancerKeySkills.setText(skills);
I am getting null response as when I execute this code. Please help and thanks in advance!
As I can see from the response, it is not returning an array or a list. However, as far as I have understood from your code, you are expecting to parse the response into a list.
The Freelancer pojo should look like the following.
public class FreeLancer {
public FreeLancerProfile freelancer;
}
If everything else is okay, it should work fine.
UPDATE:
The response should be bound to FreeLancer class, not to FreeLancerProfile class. I have shown an example, which might help. Please check.
public void onResponse(Call<FreeLancer> call, Response<FreeLancer> response) {
if (response.code() == 200) {
FreeLancer freelancer = response.body();
name = freelancer.getName();
email = freelancer.getEmail();
// Others ...
}
}
And in your FreelancerProfile pojo, you need to remove the List from the key_skills and other_skills as well. These are not lists as well.
private KeySkill key_skills;
private OtherSkill other_skills;

NullPointerException when loading JSON data with RetroFit using two queries

I'm developing an app that pulls data in JSON format from an API when a user searches a name, and can pick between searching for an artist or album. I'm using RetroFit to call the JSON data and want to use two queries in my interface.
Code to call JSON data:
public void ArtistSearch() {
String searchTerm = searchTxt.getText().toString().trim();
String searchCat = searchPick.getSelectedItem().toString().trim();
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder().baseUrl(api_url).addConverterFactory
(GsonConverterFactory.create());
final Retrofit retrofit = builder.client(httpClient.build()).build();
ArtistClient client = retrofit.create(ArtistClient.class);
Call<JSONResponse> call = client.getArtistList(searchTerm, searchCat);
call.enqueue(new Callback<JSONResponse>() {
#Override
public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
List<ArtistList> artists = response.body().getArtists();
Log.d(TAG, "Artists found");
Log.d(TAG, "Number of artists:" + artists.size());
artistAdapter = new ArtistAdapter(DiscogSearchPage.this, artists);
discogList.setAdapter(artistAdapter);
}
#Override
public void onFailure(Call<JSONResponse> call, Throwable t) {
Log.e(TAG, "Fail: " + t.getMessage());
}
});
}
Interface:
public interface ArtistClient {
#GET("/database/search")
Call<JSONResponse> getArtistList (#Query("q") String searchTerm, #Query("type") String searchCategory);
}
Currently when I try to run this I get the error
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List ...Responses.JSONResponse.getArtists()' on a null object reference.
However if I comment out the second query in the interface and remove searchCat from the code the data loads fine. I have no idea what is going on.
Any help is appreciated.
Edit: For those asking for model classes and JSON response.
JSON response class:
public class JSONResponse {
#SerializedName("results")
#Expose
private List<ArtistList> artists = null;
// Getters and setters
ArtistList class:
public class ArtistList {
#SerializedName("thumb")
#Expose
private String thumb;
#SerializedName("title")
#Expose
private String title;
#SerializedName("uri")
#Expose
private String uri;
#SerializedName("cover_image")
#Expose
private String coverImage;
#SerializedName("resource_url")
#Expose
private String resourceUrl;
#SerializedName("type")
#Expose
private String type;
#SerializedName("id")
#Expose
private int id;
// Getters and setters
JSON response, with search "Drake" and type "artist":
{
"pagination": {
"per_page": 50,
"items": 22893,
"page": 1,
"urls": {
"last": "https://api.discogs.com/database/search?%3Ftype=artist&q=drake&secret=AEmHdfwlGwPqUYQpTBVrarEtzjKsykih&key=wMWTrOWaTkfHDUQVXFSG&per_page=50&page=458",
"next": "https://api.discogs.com/database/search?%3Ftype=artist&q=drake&secret=AEmHdfwlGwPqUYQpTBVrarEtzjKsykih&key=wMWTrOWaTkfHDUQVXFSG&per_page=50&page=2"
},
"pages": 458
},
"results": [
{
"thumb": "https://img.discogs.com/Voe5_n4NEBvrIW2AczQyGb389WM=/150x150/smart/filters:strip_icc():format(jpeg):mode_rgb():quality(40)/discogs-images/A-151199-1520497976-7068.jpeg.jpg",
"title": "Drake",
"uri": "/artist/151199-Drake",
"cover_image": "https://img.discogs.com/nFMZ1bVcXA3bLHxR_0LVSrRB7iM=/456x615/smart/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/A-151199-1520497976-7068.jpeg.jpg",
"resource_url": "https://api.discogs.com/artists/151199",
"type": "artist",
"id": 151199
},
// results objects continue

What should be my class structure to parse JSON in GSON

I have the following JSON data:
{
"response": {},
"errorMessage": {
"error": [
{
"errorId": 260003,
"domain": "ads",
"subdomain": "asd",
"severity": "asd",
"category": "asd",
"message": "asdsa asd ad",
"errorName": "UnAuthorized"
}
]
}
}
Currently I have the following class structure:
public class JSONCollection
private Response response;
private ErrorMessage error;
public class Response
private String collectionId;
private String url;
public class ErrorMessage
private List<ErrorValues> error;
public class ErrorValues
private String errorId;
private String domain;
private String subdomain;
private String severity;
private String category;
private String message;
private String errorName;
I have setters/get set for all private variables
But when I do a JSONCollection cJson = gson.fromJson(JSONValue,JSONCollection.class); I get cJson as a null.
How to get it right?
I used this tool shown by #JigarJoshi to generate my schema.
The only difference I found is I had to change the class name from ErrorValues to Error

Categories