NullPointerException when loading JSON data with RetroFit using two queries - java

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

Related

rest api - PUT method does not consume the data that GET method fetches

I obtain data with GET method and try to fed them to PUT method.
And I get a bad request error.
But when I edit the JSON as below then everithing works fine.
So why does the first JSON not work?
Controller class:
#RestController
#RequestMapping("/api")
public class FileController {
private final FileService fileService;
private final DocService docService;
private final DraftFileToPresentationConverter draftFileToPresentationConverter;
#Autowired
private DocFileRelationRepository docFileRelationRepository;
#Autowired
public FileController(FileService fileService,
DocService docService,
DraftFileToPresentationConverter draftFileToPresentationConverter) {
this.fileService = fileService;
this.docService = docService;
this.draftFileToPresentationConverter = draftFileToPresentationConverter;
}
#GetMapping("/docs/files/{id}")
public ResponseEntity<FilePresentation> getDraftFile(#PathVariable Long id) {
DraftFile file = fileService.getFile(id);
FilePresentation filePresentation = draftFileToPresentationConverter.convert(file);
return new ResponseEntity<>(filePresentation, HttpStatus.OK);
}
#PutMapping("/docs/files/{id}")
public ResponseEntity<FilePresentation> updateDraftFile(#RequestBody FilePresentation filePresentation) {
fileService.update(draftFileToPresentationConverter.convert(filePresentation));
return new ResponseEntity<>(filePresentation, HttpStatus.OK);
}
DTO:
#Data
public class FilePresentation {
private Long id;
private States state;
private String name;
private String user;
private Date dateUpload;
private Long lenght;
private IdRef document;
private IdRef judgeDoc;
public String getSize()
{
Double result = Double.valueOf(lenght/1024.0/1024.0);
if(result<1)
{
result = Double.valueOf(lenght/1024.0);
if(result<1)
{
return (lenght + " байт");
}
return (result.intValue() + " Кбайт");
}
return (result.intValue() + " Мбайт");
}
}
Troublesome class:
#Data
public class IdRef {
public IdRef(Long id) {
this.id = id;
}
private Long id;
}
JSON that I get with GET method and try to fed to PUT method (and get 400 Bad Request):
{
"id": 21,
"state": "DRAFT",
"name": "DNS-list.tiff",
"user": "JSmith",
"dateUpload": null,
"lenght": 28,
"document": {
"id": 141
},
"judgeDoc": null,
"size": "28 байт"
}
JSON that DOES work
{
"id": 21,
"state": "DRAFT",
"name": "DNS-list.tiff",
"user": "JSmith",
"dateUpload": null,
"lenght": 28,
"document": 141,
"judgeDoc": null,
"size": "28 байт"
}
Try to update RequestBody with #PathVariable
The constructor in IdRef was the reason.
I removed the constructor and it works fine now, my controller consumes the first JSON without errors.

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;

How do I parse nested objects with Retrofit 2?

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
}

Android - Deserialize this JSON to a POJO

This is my JSON:
{
"results": [
{
"user_id": "1",
"item_id": "18630",
"name": "Unnamed Item",
"price": "0",
"description": "",
"created_at": "2014-01-16 15:31:36",
"thumbnail": {
"image50": "http://www.example.com/adsa.jpg",
"image100": "hhttp://www.example.com/adsa.jpg"
},...
Am I doing the deserialization right?
public class ItemListModel {
private String user_id;
private String item_id;
private String name;
private String price;
private String category;
private ArrayList<ThumbnailResponse> thumbnail;
public ItemListModel(){}
// getters
}
public class ThumbnailResponse {
private String image50;
private String image100;
public ThumbnailResponse(){
}
//getters
}
I'm just confused, when do we use ArrayList, Array or List for array or object in the JSON file?
One more thing, do I need to make results as an array too if that's the case?
As you have given
"thumbnail": {
"image50": "http://www.example.com/adsa.jpg",
"image100": "hhttp://www.example.com/adsa.jpg"
}
is not a JsonArray. So you have no need to use ThumbnailResponse as an ArrayList into ItemListModel.
Your Model should be
public class ItemListModel {
private String user_id;
private String item_id;
private String name;
private String price;
private String category;
private ThumbnailResponse thumbnail; // Not array List
public ItemListModel(){}
// getters
}
And
One more thing, do I need to make results as an array too if that's
the case?
Your main data container should be contain ArrayList of ItemListModel. Like below
ArrayList<ItemListModel> results = new ArrayList<ItemListModel>();
[] in json -> array
{} in json -> object or map
in your case
// change
private ArrayList<ThumbnailResponse> thumbnail;
// to
private Map<String,String> thumbnail;
if you want it the way you declared your java object you need to provide a transformer (depends on the framework you are using)
List<ItemListModel > ItemListModel ;
try {
Type listType = new TypeToken<List<ItemListModel >>(){}.getType();
result= (List<ItemListModel >) gson.fromJson(result, listType);
} catch (Exception e) {
Log.e("Parsing exeption", e.getLocalizedMessage(), e);
}
this should work

Categories