How to Parse nested json using Retrofit? - java
I want to parse nested json using retrofit and bind it in recyclerview. Am familiar with Parsing simple json using Retrofit. But i dont know how to parse nested json using retrofit. i am newbie to retrofit. any help will be appreciated ?
Here is that Link: http://api.wunderground.com/api/356d60036a9374e9/conditions/forecast/alert/q/22.897,88.879.json
JSON DATA :
{
"response": {
"version":"0.1",
"termsofService":"http://www.wunderground.com/weather/api/d/terms.html",
"features": {
"conditions": 1
,
"forecast": 1
}
,
"error": {
"type": "unknownfeature"
}
}
, "current_observation": {
"image": {
"url":"http://icons.wxug.com/graphics/wu2/logo_130x80.png",
"title":"Weather Underground",
"link":"http://www.wunderground.com"
},
"display_location": {
"full":"Tentulbaria, India",
"city":"Tentulbaria",
"state":"WB",
"state_name":"India",
"country":"IN",
"country_iso3166":"IN",
"zip":"00000",
"magic":"608",
"wmo":"41946",
"latitude":"22.890000",
"longitude":"88.870000",
"elevation":"11.9"
},
"observation_location": {
"full":"Kolkata, ",
"city":"Kolkata",
"state":"",
"country":"IN",
"country_iso3166":"IN",
"latitude":"22.64999962",
"longitude":"88.44999695",
"elevation":"20 ft"
},
"estimated": {
},
"station_id":"VECC",
"observation_time":"Last Updated on February 5, 5:30 PM IST",
"observation_time_rfc822":"Mon, 05 Feb 2018 17:30:00 +0530",
"observation_epoch":"1517832000",
"local_time_rfc822":"Mon, 05 Feb 2018 17:44:32 +0530",
"local_epoch":"1517832872",
"local_tz_short":"IST",
"local_tz_long":"Asia/Kolkata",
"local_tz_offset":"+0530",
"weather":"Clear",
"temperature_string":"81 F (27 C)",
"temp_f":81,
"temp_c":27,
"relative_humidity":"61%",
"wind_string":"Calm",
"wind_dir":"North",
"wind_degrees":0,
"wind_mph":0,
"wind_gust_mph":0,
"wind_kph":0,
"wind_gust_kph":0,
"pressure_mb":"1013",
"pressure_in":"29.92",
"pressure_trend":"0",
"dewpoint_string":"66 F (19 C)",
"dewpoint_f":66,
"dewpoint_c":19,
"heat_index_string":"83 F (28 C)",
"heat_index_f":83,
"heat_index_c":28,
"windchill_string":"NA",
"windchill_f":"NA",
"windchill_c":"NA",
"feelslike_string":"83 F (28 C)",
"feelslike_f":"83",
"feelslike_c":"28",
"visibility_mi":"1.7",
"visibility_km":"2.8",
"solarradiation":"--",
"UV":"-1","precip_1hr_string":"-9999.00 in (-9999.00 mm)",
"precip_1hr_in":"-9999.00",
"precip_1hr_metric":"--",
"precip_today_string":"0.00 in (0.0 mm)",
"precip_today_in":"0.00",
"precip_today_metric":"0.0",
"icon":"clear",
"icon_url":"http://icons.wxug.com/i/c/k/nt_clear.gif",
"forecast_url":"http://www.wunderground.com/global/stations/41946.html",
"history_url":"http://www.wunderground.com/history/airport/VECC/2018/2/5/DailyHistory.html",
"ob_url":"http://www.wunderground.com/cgi-bin/findweather/getForecast?query=22.64999962,88.44999695",
"nowcast":""
}
}
In that json data i want to fetch the following json data:
"current_observation": {
"image": {
"url":"http://icons.wxug.com/graphics/wu2/logo_130x80.png",
"title":"Weather Underground",
"link":"http://www.wunderground.com"
},
First, create Model i.e POJO class to parse your response
It will look something like this
package app.com.socket;
/**
* Created by akshay.katariya on 05-Feb-18.
*/
import com.google.gson.annotations.SerializedName;
public class Pojo
{
#SerializedName("response")
public Response response;
#SerializedName("current_observation")
public Current_observation current_observation;
public static class Features {
#SerializedName("conditions")
public int conditions;
#SerializedName("forecast")
public int forecast;
}
public static class Error {
#SerializedName("type")
public String type;
}
public static class Response {
#SerializedName("version")
public String version;
#SerializedName("termsofService")
public String termsofService;
#SerializedName("features")
public Features features;
#SerializedName("error")
public java.lang.Error error;
}
public static class Image {
#SerializedName("url")
public String url;
#SerializedName("title")
public String title;
#SerializedName("link")
public String link;
}
public static class Display_location {
#SerializedName("full")
public String full;
#SerializedName("elevation")
public String elevation;
}
public static class Observation_location {
#SerializedName("elevation")
public String elevation;
}
public static class Estimated {
}
public static class Current_observation {
#SerializedName("image")
public Image image;
#SerializedName("display_location")
public Display_location display_location;
#SerializedName("observation_location")
public Observation_location observation_location;
#SerializedName("estimated")
public Estimated estimated;
#SerializedName("nowcast")
public String nowcast;
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public Display_location getDisplay_location() {
return display_location;
}
public void setDisplay_location(Display_location display_location) {
this.display_location = display_location;
}
public Observation_location getObservation_location() {
return observation_location;
}
public void setObservation_location(Observation_location observation_location) {
this.observation_location = observation_location;
}
public Estimated getEstimated() {
return estimated;
}
public void setEstimated(Estimated estimated) {
this.estimated = estimated;
}
public String getNowcast() {
return nowcast;
}
public void setNowcast(String nowcast) {
this.nowcast = nowcast;
}
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
public Current_observation getCurrent_observation() {
return current_observation;
}
public void setCurrent_observation(Current_observation current_observation) {
this.current_observation = current_observation;
}
}
Generate proper getter & setter
Then in your main class create
Pojo model = new Pojo();
Call API & Parse the response
model = response.body(); // retrofit api call parsing
You have all your values ready to set
mEditText.setText(model.getCurrent_observation().image.url);
You can add json convertor to Retrofit and you can parse it.
Parsing Nested Json
Retrofit best tutorial i have found till now.
Retrofit complete turoial.
This will help you in all your concerns related to Retrofit.
Create mode like this and in your response type pass the model it will automatically parse the json into model for image data
public class Example {
#SerializedName("current_observation")
#Expose
private CurrentObservation currentObservation;
public CurrentObservation getCurrentObservation() {
return currentObservation;
}
public void setCurrentObservation(CurrentObservation currentObservation) {
this.currentObservation = currentObservation;
}
public static class CurrentObservation {
#SerializedName("image")
#Expose
private Image image;
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
}
public static class Image {
#SerializedName("url")
#Expose
private String url;
#SerializedName("title")
#Expose
private String title;
#SerializedName("link")
#Expose
private String link;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
}
sample api call
public interface Service {
#GET("users/info")
Call<Example> getInfo();
}
Edit:
you can call this retrofit function anywhere in your MainActivity and use the data.
public void getImageData(){
Call<Example> call = apiService.getInfo();
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
//this is how you can use the parsed model
Example info = response.body();
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
//handle the error here
}
}
Simple way is to receive response as a JsonObject / JsonArray and parse it to create object with your own Model class.
So that you can avoid unwanted data, yes your code will be lengthy but its worth it. You can format and store the data. Easy to display.
Also "Opt" methods can be used instead of "Get", so NULL can be avoided.
Related
How to solve E/error: End of input at line 1 column 1 path $ in android studio
When I try to call rest API in the android studio I get an error that: E/error: End of input at line 1 column 1 path $ I use firebase for the database and retrofit2 library. But when I call the values a go to the firebase database and call the onFailure in call.enqueue() method. public class APis { public static final String URL = "http://192.168.178.43:8081/api/"; public static userService setuser() { return client.getClient(URL).create(userService.class); } } public interface userService { #Headers("Content-Type: application/json") #POST("signup") Call<userlog> adduser(#Body userlog userlog); } public class userlog { #SerializedName("email") #Expose private String emial_; #SerializedName("password") #Expose private String password_; #SerializedName("name") #Expose private String name_; public userlog() { } public userlog(String emial_, String password, String name_) { this.emial_ = emial_; this.password_ = password; this.name_ = name_; } public String getEmial_() { return emial_; } public void setEmial_(String emial_) { this.emial_ = emial_; } public String getPassword_() { return password_; } public void setPassword_(String password_) { this.password_ = password_; } public String getName_() { return name_; } public void setName_(String name_) { this.name_ = name_; } } public void setPassword_(String password_) { this.password_ = password_; } } private void adduser_(userlog userll) { service = APis.setuser(); Call<userlog> call = service.adduser(userll); call.enqueue(new Callback<userlog>() { #Override public void onResponse(Call<userlog> call, Response<userlog> response) { if (response.isSuccessful()) { Toast.makeText(getApplicationContext(), "Success", Toast.LENGTH_SHORT).show(); /* userdetails.setUserid(firebaseAuth.getUid()); userdetails.setEmail_(emailId.getText().toString()); startActivity(new Intent(SignupActivity.this, MainnewActivity.class));*/ } } #Override public void onFailure(Call<userlog> call, Throwable t) { Log.e("error", t.getMessage()); Toast.makeText(getApplicationContext(), "not Successdd", Toast.LENGTH_SHORT).show(); } }); } when I call "adduser_(userll)" method, I get a notification that "not Successdd".
The problem related to retrofit, i think the problem because the response of the call come as null or empty you can create NullConverterFactory.class : public class NullConverterFactory extends Converter.Factory { #Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations); return new Converter<ResponseBody, Object>() { #Override public Object convert(ResponseBody body) throws IOException { if (body.contentLength() == 0) return null; return delegate.convert(body); } }; } } and add to the create of the retrofit baseUrl(Config.URL+"/") .client(okHttpClient) // -----add here------- .addConverterFactory(new NullConverterFactory()) //--------------------- .addConverterFactory(GsonConverterFactory.create()) .build()
Retrofit expecting an array not an object
Hi I am new to the Retrofit 2.0 library, I am having problems parsing some json. I have looked at some other solutions on Stackoverflow but not having much luck with my problem. I am trying to call an api from android with retrofit 2.0.But it is throwing error Expected BEGIN_ARRAY but was BEGIN_OBJECT. Any ideas what im doing wrong? Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 2 column 2 path $ Fragment.java ListView superListView; static Retrofit retrofit = null; #Override public void onCreate (#Nullable Bundle savedInstanceState) { super.onCreate (savedInstanceState); connect2(); } private void connect2() { if (retrofit == null) { retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); } HomeApiService movieApiService = retrofit.create(HomeApiService.class); Call<List<Movie3>> call = movieApiService.popularMovies2(); call.enqueue(new Callback<List<Movie3>>() { #Override public void onResponse(Call<List<Movie3>> call, Response<List<Movie3>> response) { List<Movie3> myheroList2 = response.body(); String[] oneHeroes = new String[myheroList2.size()]; for (int i2 = 0; i2 < myheroList2.size(); i2++) { oneHeroes[i2] = myheroList2.get(i2).getTitle(); } superListView.setAdapter(new ArrayAdapter<String>(getActivity().getApplicationContext(), android.R.layout.simple_list_item_2, oneHeroes)); } #Override public void onFailure(Call<List<Movie3>> call, Throwable throwable) { Log.e("Error: ", throwable.toString()); } }); } HomeApiService.java public interface HomeApiService { #GET("movie/test") Call<List<Movie3>> popularMovies2 (); } Movie3.java public class Movie3 { #SerializedName("title") private String title; public Movie3(String title) { this.title = title; } public String getTitle() { return title; } }
Ok...First of all your POJO for accepting the response is not correct. This is a crucial part while accepting a response. Your POJO should look like this : public class Result{ public boolean adult; public String backdrop_path; public List<int> genre_ids; public int id; public String original_language; public String original_title; public String overview; public double popularity; public String poster_path; public String release_date; public String title; public boolean video; public double vote_average; public int vote_count; } public class Root{ public int page; public List<Result> results; public int total_pages; public int total_results; } Where root is the class and result is another class. As you can see the response contains result as a list. And also the HomeApiService should look like this: public interface HomeApiService { #GET("movie/popular?api_key=ffd597419be5a256066dc51c49bc659f") Call<Root> popularMovies2 (); } Hope you got the point. Add necessary getters and setters. Normally it's easy to use a JSON to POJO online converter. Here is one: https://json2csharp.com/json-to-pojo ..... paste the response in JSON and get the POJO converted.
Getting nested array from JSON using Jackson lib - Java
I've got a JSON like this: { "result": [ { "reservation_id": 101, "euro_fee": 11.00, "hotel_id": 1 }, { "reservation_id": 102, "euro_fee": 12.00, "hotel_id": 2 }, { "reservation_id": 103, "euro_fee": 13.00, "hotel_id": 3 } ], "meta": { "ruid": "0001=" } } and I'm trying to use Jackson (with Spring Boot) for parse and bind it. Here is my POJO's: Response.java public class Response { private Result result; private Meta meta; public Response() { } public Result getResult() { return result; } public void setResult(Result result) { this.result = result; } public Meta getMeta() { return meta; } public void setMeta(Meta meta) { this.meta = meta; } } Meta.java public class Meta { private String ruid; public Meta() { } public String getRuid() { return ruid; } public void setRuid(String ruid) { this.ruid = ruid; } } Result.java public class Result { private Booking[] results; public Result() { } public Booking[] getResults() { return results; } public void setResult(Booking[] results) { this.results = results; } } Booking.java public class Booking { private long reservation_id; private long hotel_id; private Double euro_fee; public Booking() { } public long getReservation_id() { return reservation_id; } public void setReservation_id(long reservation_id) { this.reservation_id = reservation_id; } public long getHotel_id() { return hotel_id; } public void setHotel_id(long hotel_id) { this.hotel_id = hotel_id; } public Double getEuro_fee() { return euro_fee; } public void setEuro_fee(Double euro_fee) { this.euro_fee = euro_fee; } } I can get ruid from meta using: // getting JSON from REST String response = restTemplate.postForObject(resourceURL, httpEntity, String.class); // use jackson ObjectMapper mapper = new ObjectMapper(); Response theResponse = mapper.readValue(response, Response.class); System.out.println("getRuid: " + theResponse.getMeta().getRuid()); but I can't get objects or single item from nested array. When I'm trying to get array of items I'm getting error: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of out of START_ARRAY token at [Source: (String)... I know this one should be easy, but I'm using Jackson for the first time and the problem might be somewhere in the deep.
Change your Response class. Try with this: public class Response { private List<Booking> result; private Meta meta; //getter setter }
Retrofit parsing JSON response null values Android
Hi i cant work out why I am getting null values for my response. Im using Retrofit library on Android. raw json { "images": [ { "image": { "name": "nike adver", "url": "http:\/\/wallpaperbarcelona.com\/wp-content\/uploads\/2013\/07\/neymar-nike-advert.jpg", "type": "photo" } }] } // interface public interface PromoImagesAPI { #GET("/FriendsCMS/images/?type=photo&format=json") void promoImages(Callback<ImagesObject> callback); } request function private void requestNewsData(String uri) { RestAdapter api = new RestAdapter.Builder().setEndpoint(ENDPOINT).build(); PromoImagesAPI restapi = api.create(PromoImagesAPI.class); restapi.promoImages(new Callback<Images>() { #Override public void success(Images imageObjects, Response response) { System.out.println("webservice " +response.getUrl()); for (int i = 0; i < imageObjects.images.size(); i++) { System.out.println("webservice " +imageObjects.getImages().get(i).getUrl()); } } #Override public void failure(RetrofitError error) { System.out.println("webservice " + error); } }); } Pojo public class ImagesObject { public List<Images> images; public class Images { public String name; public String url; public String type; public String getName() { return name; } public String getUrl() { return url; } public String getType() { return type; } public void setName(String name) { this.name = name; } public void setUrl(String url) { this.url = url; } public void setType(String type) { this.type = type; } } } The thing is the amount of elements in the for loop is correct, i have tested that, the values are all null. Have i missed something, any help would be gratefully appreciated . thanks
use http://www.jsonschema2pojo.org/ to create your java object model and do the following to call public interface PromoImagesAPI { #GET("/FriendsCMS/images/?type=photo&format=json") void promoImages(Callback<Images> callback);
Yes, I answer by agreeing. I also missed the #Expose annotation. This can happen specially when using third party tools to convert from json to kotlin or java classes. I also used Gson to convert from json when doing unit testing and everything was passing gracefully until I ran the app and everything came back with null values
MusixMatch API and GSON: Using track.snippet.get instead of track.lyrics.get
I am working on the final project for an intro to Java class. Part of the project involves getting a lyric snippet from MusixMatch using their API. I am able to get lyrics from the API using track.lyrics.get, but cannot get snippets using tracks.snippet.get. I started with a Java wrapper found here: https://github.com/sachin-handiekar/jMusixMatch and added my own classes to get a snippet based on the track.snippet.get API method. When I run the program I get this error: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 102 path $.message.body My getSnippet method and applicable classes follow. They are based on the getLyrics method and classes found in the original wrapper. public Snippet getSnippet(int trackID) throws MusixMatchException { Snippet snippet = null; SnippetGetMessage message = null; Map<String, Object> params = new HashMap<String, Object>(); params.put(Constants.API_KEY, apiKey); params.put(Constants.TRACK_ID, new String("" + trackID)); String response = null; response = MusixMatchRequest.sendRequest(Helper.getURLString( Methods.TRACK_SNIPPET_GET, params)); Gson gson = new Gson(); try { message = gson.fromJson(response, SnippetGetMessage.class); } catch (JsonParseException jpe) { handleErrorResponse(response); } snippet = message.getContainer().getBody().getSnippet(); return snippet; } The Snippet Class package org.jmusixmatch.entity.snippet; import com.google.gson.annotations.SerializedName; /** * Created by kyledhebert on 4/30/15. * Objects of this clas represent a lyric snippet from the * MusixMatch API. */ public class Snippet { #SerializedName("snippet_language") private int snippetLanguage; #SerializedName("restricted") private int restricted; #SerializedName("instrumental") private int instrumental; #SerializedName("snippet_body") private String snippetBody; #SerializedName("script_tracking_url") private String scriptTrackingURL; #SerializedName("pixel_tracking_url") private String pixelTrackingURL; #SerializedName("html_tracking_url") private String htmlTrackingURL; #SerializedName("updated_time") private String updatedTime; public int getSnippetLanguage() { return snippetLanguage; } public void setSnippetLanguage(int snippetLanguage) { this.snippetLanguage = snippetLanguage; } public int getRestricted() { return restricted; } public void setRestricted(int restricted) { this.restricted = restricted; } public int getInstrumental() { return instrumental; } public void setInstrumental(int instrumental) { this.instrumental = instrumental; } public String getSnippetBody() { return snippetBody; } public void setSnippetBody(String snippetBody) { this.snippetBody = snippetBody; } public String getPixelTrackingURL() { return pixelTrackingURL; } public void setPixelTrackingURL(String pixelTrackingURL) { this.pixelTrackingURL = pixelTrackingURL; } public String getScriptTrackingURL() { return scriptTrackingURL; } public void setScriptTrackingURL(String scriptTrackingURL) { this.scriptTrackingURL = scriptTrackingURL; } public String getHtmlTrackingURL() { return htmlTrackingURL; } public void setHtmlTrackingURL(String htmlTrackingURL) { this.htmlTrackingURL = htmlTrackingURL; } public String getUpdatedTime() { return updatedTime; } public void setUpdatedTime(String updatedTime) { this.updatedTime = updatedTime; } } The SnippetGetBody class: package org.jmusixmatch.entity.snippet.get; import com.google.gson.annotations.SerializedName; import org.jmusixmatch.entity.snippet.Snippet; public class SnippetGetBody { #SerializedName("snippet") private Snippet snippet; public Snippet getSnippet() { return snippet; } public void setSnippet(Snippet snippet) { this.snippet = snippet; } } The SnippetGetContainer class: package org.jmusixmatch.entity.snippet.get; import com.google.gson.annotations.SerializedName; import org.jmusixmatch.entity.Header; public class SnippetGetContainer { #SerializedName("body") private SnippetGetBody body; #SerializedName("header") private Header header; public SnippetGetBody getBody() { return body; } public void setBody(SnippetGetBody body) { this.body = body; } public Header getHeader() { return header; } public void setHeader(Header header) { this.header = header; } } The SnippetGetMessage class: package org.jmusixmatch.entity.lyrics.get; import com.google.gson.annotations.SerializedName; public class SnippetGetMessage { #SerializedName("message") private SnippetGetContainer container; public void setContainer(SnippetGetContainer container) { this.container = container; } public SnippetGetContainer getContainer() { return container; } }
I was not able to reproduce your exact error message, but I did find the following error: snippet_language is a String, not an int. Change the type (and associated getters and setters) to: #SerializedName("snippet_language") private String snippetLanguage; I used the sample Json response from here to make this work. If these two changes don't fix your problem, please edit your question with the actual Json response that is making your program not work.