In the first case when it returns success true, everything works, the problem when it gets success boolean is false, then the error:
How Retrofit Response Array or Object either.
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 26 path $ .data
Can it be done with one reponse class?
Json response:
{
"success": true,
"data": {
"message": "User created",
}
}
Json response:
{
"success": false,
"data": [
{
"code": "existing_user_login",
"message": "User Exist !"
}
]
}
Code:
public class Response {
public Boolean success;
public Data data;
public Boolean isSuccess() { return success; }
public Data getData() {
return data;
}
public class Data {
public String code;
public String message;
public String getMessage() { return message; }
public String getCode() { return code; }
}
}
you could use Object data first, and judge it before use it, like following:
public class Response {
public Boolean success;
public Object data;
public Boolean isSuccess() { return success; }
public Object getData() {
if(data instanceof Data){
//do something
}else if (data instanceof List) {
//do something
}else {
//do something
}
return data;
}
public class Data {
public String code;
public String message;
public String getMessage() { return message; }
public String getCode() { return code; }
}
}
Using ObjectMapper you can do it by configuring deserialization features as below:
// import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objMapper = new ObjectMapper();
// this is to accept single element as array
objMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
// this is to handle if any property is missing
objMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Response resp = objMapper.readValue(jsonString, Response.class);
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.
I am trying to parse a JSON file in Java. But the format of the JSON file is complex (at least for me) and hence I have run into a roadblock.
You may read the file-
Reddit JSON file
Within the "reddit" file, the 'key' I am interested in is "body" which is nested deep within the JSON array within the file.
I am using Jackson for parsing and deserialization of the JSON to a Java object and my code is as follows-
class Reddit
{
private String kind;
private String data;
public String getData()
{
return this.data;
}
public void setData(String data)
{
this.data = data;
}
public String getKind()
{
return this.data;
}
public void setKind(String data)
{
this.data = data;
}
}
public class Attempting_Read_JSON
{
public static void main(String[] args)
{
// ObjectMapper can parse JSON into Java Objects, can do vice-versa and more-
ObjectMapper OM = new ObjectMapper();
try
{
out.println("\nInside try block.\n");
File json_file = new File("src/main/resources/data/reddit/redditdump.json");
Reddit[] R = OM.readValue(json_file, Reddit[].class);
out.println("\nRead from JSON file to 'Reddit class object'\n");
//out.println("\nObtained JSON- " + R.getData());
}
catch (Exception E)
{
E.printStackTrace();
}
out.println("\n\nProgram terminated successfully!\n");
}
}
But on executing the program, it keeps giving me errors.
Any suggestions on how I might get the value associated with the key- 'body'?
Thanks!
In your JSON file "data": is an object and you are trying to assign to String in Reddit class through private String data;.
That's why you are getting this error.
You either need to create a class say Data with members contest_mode, banned_by.... or use JSON Object to parse it.
Also why are you setting data here use kind
public String getKind()
{
return this.data;
}
public void setKind(String data)
{
this.data = data;
}
EDIT:
For simplicity I will give you small example:
JSON file:
[
{
"kind": "Listing",
"data": {
"contest_mode": false
}
},
{
"kind": "Listing",
"data": {
"contest_mode": false
}
}
]
Your code:
public class Attempting_Read_JSON {
public static void main(String[] args) {
ObjectMapper OM = new ObjectMapper();
try {
out.println("\nInside try block.\n");
File json_file = new File(
"src/main/resources/data/reddit/redditdump.json");
Reddit[] R = OM.readValue(json_file, Reddit[].class);
out.println("\nRead from JSON file to 'Reddit class object'\n" + R[0].toString());
// out.println("\nObtained JSON- " + R.getData());
} catch (Exception E) {
E.printStackTrace();
}
out.println("\n\nProgram terminated successfully!\n");
}
}
class Reddit {
private String kind;
#Override
public String toString() {
return "Reddit [kind=" + kind + ", data=" + data.toString() + "]";
}
private Data data;
public Data getData() {
return this.data;
}
public void setData(Data data) {
this.data = data;
}
public String getKind() {
return this.kind;
}
public void setKind(String kind) {
this.kind = kind;
}
}
class Data
{
boolean contest_mode;
public boolean isContest_mode() {
return contest_mode;
}
public void setContest_mode(boolean contest_mode) {
this.contest_mode = contest_mode;
}
#Override
public String toString() {
return "[contest_mode=" + contest_mode + "]";
}
}
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.
This is the JSON string:
{
"d":{
"results":[
{
"__metadata":{
"uri":"http://blabla1",
"type":"type1"
},
"Synonym":"miami"
},
{
"__metadata":{
"uri":"http://blabla2",
"type":"type2"
},
"Synonym":"florida"
}
]
}
}
This is the code:
public class Test{
#JsonIgnoreProperties(ignoreUnknown = true)
public static class d {
public List<Results> results;
public d() {
results = new ArrayList<Results>();
}
public static class Results {
public Metadata __metadata;
public String Synonym;
public Results() {}
}
public static class Metadata {
public String uri;
public String type;
public Metadata() {}
}
}
}
With the following mapper:
ObjectMapper mapper = new ObjectMapper();
Test.d readValue = mapper.readValue(jsonString, Test.d.class);
for (Test.d.Results k : readValue.results) {
System.out.println("synonym: "+k.Synonym);
}
It gives me no error, just an empty arraylist of results...
p.s. i have made a lot of changes in between time, sorry for the inconvenience
you must create an object that fits with the jSon answer, something like this (not tested):
class d {
public List<Results> results;
public d() {
}
}
class Results {
public Metadata metadata;
public String synonym;
public Results() {
}
}
class Metadata {
public String uri;
public String type;
public Metadata() {
}
}
Hope it helps!
I have managed to solve it.
i forgot to make setters and getters for class 'd'.
public class Test {
private d d;
public d getD() {return d;}
public void setD(d d) {this.d = d;}
public static class d{
private List<Results> results;
public List<Results> getResults() {return results;}
public void setResults(List<Results> results) {this.results = results;}
}
public static class Results {
public Metadata __metadata;
public String Synonym;
}
public static class Metadata {
private String uri;
private String type;
public String getUri() {return uri;}
public void setUri(String uri) {this.uri = uri;}
public String getType() {return type;}
public void setType(String type) {this.type = type;}
}
}
This is the map:
Test test = mapper.readValue(json, KeyPhrase.class);
System.out.println("cp");
for(Test.Results res : test.getD().getResults()){
System.out.println(res.Synonym);
}