Retrofit: Parsing JSON to multiple Java objects using GSON - java

I'm trying to create an Photos ArrayList of Photo objects. I want to separate the Photo class from the Photos class because it's becoming unreadable. The problem is that I'm no longer getting any data back
It is worth noting that this WAS working when I had the Photo class nested in Photos class as follows:
public class Photos {
#Expose private int page;
#Expose private int pages;
#Expose private int perpage;
#Expose private String total;
#Expose private ArrayList<Photo> photo = new ArrayList<Photo>();
//helpers
public ArrayList<Photo> getPhotos(){return photo;}
public void setPhotos(ArrayList<Photo> photos){this.photo = photos;}
//want to put this in it's own class
public class Photo {
//helpers
#Override
public String toString(){
return title;
}
public String getUrl(){return url_s;}
//GSON fields
#Expose private String id;
#Expose private String owner;
#Expose private String secret;
#Expose private String server;
#Expose private int farm;
#Expose private String title;
#Expose private int ispublic;
#Expose private int isfriend;
#Expose private int isfamily;
#Expose private String url_s;
}
}
Retrofit:
new Callback<PhotosResponse>() {
#Override
public void success(PhotosResponse photosResponse, Response response) {
bus.post(new ImagesReceivedEvent(photosResponse.getPhotosObject().getPhotos()));
}
The parameters matched the JSON exactly and I received an arraylist through the following response:
public class ImagesReceivedEvent {
private ArrayList<Photo> result;
public ImagesReceivedEvent(ArrayList<Photo> result){
Log.i(TAG, "arraylist for presenter");
this.result = result;
}
public ArrayList<Photo> getResult(){return result;}
}
This is where the response was received, but no longer:
public class PhotosResponse {
#Expose private Photos photos;
#Expose private String stat;
public Photos getPhotosObject(){return photos;}
public void setPhotosObject(Photos photos) {
this.photos = photos;
}
public String getStat() {
return stat;
}
public void setStat(String stat) {
this.stat = stat;
}
}
How can I separate these classes and still get the arrayList as a response? I want to separate them because I plan on using Parcelable on each class there are some hurdles I'd need to jump over when having the inner class. Any guidance would be much appreciated! Thanks.
Edit: Here is the JSON payload:
{ "photos": { "page": 1, "pages": 10, "perpage": 100, "total": "1000",
"photo": [
{ "id": "18473086614", "owner": "130897025#N07", "secret": "b3c684c356", "server": "259", "farm": 1, "title": "My travels circa 2013. Summer days on #charlesbridge #charlesbridgeprague #prauge. upbound#upbound.net || 718-754-5850 #photographer #photography #canonphotography #canon #canondslr #canon600d #dslr #600d #travelphotography #europe #upboundonline #canonre", "ispublic": 1, "isfriend": 0, "isfamily": 0 },
{ "id": "18473090744", "owner": "131790787#N07", "secret": "2734055852", "server": "3705", "farm": 4, "title": "Untitled", "ispublic": 1, "isfriend": 0, "isfamily": 0 },
{ "id": "18473091934", "owner": "61308696#N00", "secret": "b40dbfcf15", "server": "401", "farm": 1, "title": "Climbing Acatenango", "ispublic": 1, "isfriend": 0, "isfamily": 0 },
{ "id": "18473092714", "owner": "39055811#N08", "secret": "e51f5a183b", "server": "3936", "farm": 4, "title": "DSCF1735.jpg", "ispublic": 1, "isfriend": 0, "isfamily": 0 }
] }, "stat": "ok" }

It turns out that I made a mistake on the naming convention:
#Expose private ArrayList<Photo> mPhotos = new ArrayList<Photo>();
I had renamed photo to mPhotos on the arrayList but forgot that GSON needs the EXACT names of the JSON fields (Which is originally photo). I changed it back to "photo" and now the arrayList populates on my Photos object.
I need to emphasize to myself that all fields MUST match the JSON naming convention.
Thanks.

When using Retrofit and GSON there's no way to "separate" your response into different classes, you must later do it yourself. Receive your big object with all the info, then unwrap it to the needed classes.
On a project I did I had a specific package with Wrapper classes to get responses from Retrofit, then later I'd manually map it to the Objects I actually needed. Hope it helps.

Related

Parse Response via rest template exchange

How to create a POJO class Of below mentioned JSOn Structure by a REST Service. Using RestTemplate.exchange i need to parse this in my java.class file and get approved_by value and use in java code. Below is the response structure:
{
"approval_rules_overwritten": true,
"rules": [
{
"id": 1,
"name": "Test",
"rule_type": "RuleTest",
"approvals_required": 2,
"approved_by": [
{
"id": 2,
"name": "ABC",
"username": "ABC_XYZ",
"state": "active",
}
],
"approved": false
}
]
}
Did you create a class representing this JSON ?
In quick in the same class file (different classes would be best in different files)
public class Response {
private Boolean approval_rules_overwritten;
private List<Rule> rules;
public static class Rule {
private Integer id;
private String name;
private String rule_type;
private Integer approvals_required;
private List<Approval> approved_by;
private Boolean approved;
public static class Approval {
private String id;
private String name;
private String username;
private String state;
}
}
}
Also remember to add getters and setters on each class.
Now you can do your classic request:
ResponseEntity<Response> = restClient.exchange(request, Response.class);
and you would get your response.
You now only have to build your rest template and also to catch exceptions that could be thrown from the exchange request.

how to deserialize with the GSON library a Json with nested objects and show them in a TextView

I want to deserialize a Json with nested objects using the Gson library. I'm trying to get the data from the api of clash royale.
I've got the first Gson data printed on a TextView, but I'm finding it impossible to do the same with nested objects. Let me give you an example:
[
{
"tag": "Whatever",
"name": "Whatever",
"trophies": 5262,
"rank": 99,
"arena": {
"name": "Whatever",
"arena": "Whatever",
"arenaID": 14,
"trophyLimit": 4700
},
"clan": {
"tag": "Whatever",
"name": "Whatever",
"role": "Whatever",
"donations": 23,
"donationsReceived": 44,
"donationsDelta": -20,
"badge": {
"name": "Whatever",
"category": "Whatever",
"id": 16003333,
"image": "https://royaleapi.github.io/cr-api-assets/badges/Whatever.png"
}
I can show the tag, the name and the trophies, but I am unable to access the data inside the clan object.
to print this data in TextView I simply declare a class where I host them, for example like this:
public class ClashData implements Serializable {
public String tag;
public String name;
public int trophies;
}
Then I connect it to TextView by placing a TextView in the corresponding layout and putting the following lines of code:
public ClashData data;
Afterwards, inside the onCreate
data =(ClashData)getIntent().getSerializableExtra("data");
And finally
nameUser.setText("Name ", data.name);
tagUser.setText("Tag " + data.tag);
trophiesUser.setText("Trophies " + data.trophies);
I've tried everything to extract data from nested objects, but I don't know how to do it, can you help me?
You can modify Clan POJO as shown below:
public class Clan {
#SerializedName("tag")
#Expose
public String tag;
#SerializedName("name")
#Expose
public String name;
#SerializedName("role")
#Expose
public int role;
#SerializedName("donation")
#Expose
public int donations;
/*Create getter and setter methods for all the instance variables.*/
}
Now add the above class to your ClashData as shown below:
public class ClashData {
#SerializedName("tag")
#Expose
public String tag;
#SerializedName("name")
#Expose
public String name;
#SerializedName("trophies")
#Expose
public int trophies;
#SerializedName("clan")
#Expose
public Clan clanData; // add this line to your code
/*Create getter and setter methods for all the instance variables.*/
}
Now you can use Gson library as shown below:
Gson gson = new Gson();
ClashData data = gson.fromJson(jsonData,ClashData.class);
Clan clanData = data.getClanData();
Have referred to this while answering the question.

Json parse error on what looks to be good string (Expected BEGIN_OBJECT but was BOOLEAN)

So, we're working in java, the object being deserialized is structured as so
#ApiModel(description = "Container for collection of Patient Search results")
public class PatientSearchResultListCMRefactor {
private final List<PatientSearchResultCMRefactor> patientSearchResults;
private final int totalSearchResults;
//getters, toString, etc
}
The object referenced in that object is as follows
public final class PatientSearchResultCMRefactor {
private float score;
private String id;
private String firstName;
private String lastName;
private String setupDate;
private Optional<AddressCM> address;
private Optional<Boolean> active;
private Optional<String> dateOfBirth;
private Optional<String> email;
private Optional<String> patientExternalId;
private Optional<String> patientReference;
private Optional<String> phoneNumber;
private Optional<String> currentAverageDaysUsed;
private Optional<Double> currentAverageHoursUsed;
private Optional<PatientOrganizationCM> organization;
private Optional<PatientOrganizationCM> topOrganization;
private Optional<ImmutableSet<PatientClinicianCM>> clinicians;
private Optional<ImmutableSet<PatientLocationCMRefactor>> locations;
private Optional<ImmutableSet<PatientDeviceCM>> devices;
private Optional<String> matchesBy;
//setters, getters, left-handed smoke-shifters, constructors
}
the code to deserialize it is
Gson gson = new Gson();
System.out.println(response.getBody());
String jsonTemp = response.getBody();
System.out.println(jsonTemp);
PatientSearchResultListCMRefactor resultListCM = gson.fromJson(jsonTemp, PatientSearchResultListCMRefactor.class);
The full string being deserialized is
{
"patientSearchResults": [
{
"score": 0.7582117,
"id": "3910dc3d-913b-4862-aee5-4610d2c2981f",
"firstName": "Nelda",
"lastName": "Dixon 36",
"setupDate": "2018-07-19",
"address": {
"streetAddress": "0545 Route 202",
"city": "Joliet",
"stateProvince": "SC",
"zipCode": "87636",
"countryCode": "USA"
},
"active": true,
"dateOfBirth": "1971-01-16",
"email": "n_dixon_RRAwyqXKIp#example.com",
"patientExternalId": "6bc63dfa-1106-40c9-875a-a74368cf5189",
"patientReference": null,
"phoneNumber": "365-177-2753",
"currentAverageDaysUsed": null,
"currentAverageHoursUsed": null,
"organization": null,
"topOrganization": null,
"clinicians": [
],
"locations": null,
"devices": [
],
"matchesBy": null
}
],
"totalSearchResults": 17
}
The error is
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BOOLEAN at line 1 column 289
which is right after
"address": {
"streetAddress": "0545 Route 202",
"city": "Joliet",
"stateProvince": "SC",
"zipCode": "87636",
"countryCode": "USA"
},
"active": true,
in the string, with the comma after "true" being column 289 (for clarification, line and column numbers are from before I prettyprinted the string). I think it's expecting a new object, but address is a sub-object nested in the top object, and sub-objects never go more than 1 level deep, though per the PatientSearchResultCMRefactor model there are multiple sub-objects. I'm a tester, and this is the body of a ReST API call, so I can't really change the format of the response. All I can do is try to parse it as-is. Any tips?
UPDATE: It turns out, gson has real trouble with optional variables and immutable sets. Once I changed PatientResultCMRefactor to
public final class PatientSearchResultCMRefactor {
private float score;
private String id;
private String firstName;
private String lastName;
private String setupDate;
private AddressCM address;
private Boolean active;
private String dateOfBirth;
private String email;
private String patientExternalId;
private String patientReference;
private String phoneNumber;
private String currentAverageDaysUsed;
private Double currentAverageHoursUsed;
private PatientOrganizationCM organization;
private PatientOrganizationCM topOrganization;
private Set<PatientClinicianCM> clinicians;
private Set<PatientLocationCMRefactor> locations;
private Set<PatientDeviceCM> devices;
private String matchesBy;
//getters, setters, etceterrers
}
Everything deserializes perfectly. Posting this as an answer rather than updating the question to make it easier for others to find in the future.

How to access of a especific element in the response of Retrofit

I'm querying an API with Retrofit, where the answer is this
[
{
"Id": "BT00",
"Text": "Registrarme"
},
{
"Id": "BT01",
"Text": "Iniciar sesiĆ³n"
},
{
"Id": "BT02",
"Text": "Siguiente"
},
{
"Id": "BT03",
"Text": "Si"
},
{
"Id": "BT04",
"Text": "No"
}
]
and the body response look like this screenshot
This call is stored in a ArrayList.
#SerializedName("Id")
#Expose
private String id;
#SerializedName("Text")
#Expose
private String text;
//Getters&Setters
My answer is, how to access of elements of the response?
I tried the following ways but it does not work
apptext_id.setText(response.body().get(0).toString());
Logger.d("Body %s", response.body().get(0).toString());
Logger.d("Body %s", response.body().get(0));
the answer look like this
What you need to do to get the first element of the list is: response.body().get(0).getText();
If you need to get each item, you need to:
if (response.isSuccessful()) {
List<AppTextModel> list_elements = response.body();
for (AppTextModel item : list_elements) {
Logger.d("Body %s", item.getText()); // print every text item in list
}
}
You also need in the POJO class:
public class AppTextModel {
#SerializedName("Id")
#Expose
private String id;
#SerializedName("Text")
#Expose
private String text;
//Getters&Setters
public String getText() {
return text;
}
}

Unable to deserialize a JSON response from SonarQube using the gson library

I'm trying to use gson to deserialize some data that I'm getting back from a SonarQube API on various code metrics. This is an example of the raw JSON coming back from the server:
{
"component": {
"id": "c5fc9d6k-e28b-4ea0-8922-df18c7e07ac1",
"key": "APP:master",
"name": "master",
"qualifier": "TRK",
"measures": [
{
"metric": "coverage",
"value": "19.9",
"periods": [
{
"index": 1,
"value": "0.09999999999999787"
},
{
"index": 2,
"value": "0.09999999999999787"
},
{
"index": 3,
"value": "0.6999999999999993"
},
{
"index": 4,
"value": "8.7"
}
]
},
{
"metric": "overall_coverage",
"value": "55.7",
"periods": [
{
"index": 1,
"value": "0.0"
},
{
"index": 2,
"value": "0.0"
},
{
"index": 3,
"value": "3.0"
},
{
"index": 4,
"value": "55.7"
}
]
},
{
"metric": "ncloc",
"value": "1089127",
"periods": [
{
"index": 1,
"value": "3835"
},
{
"index": 2,
"value": "3835"
},
{
"index": 3,
"value": "-74350"
},
{
"index": 4,
"value": "102501"
}
]
}
]
}
}
I'm attempting to deserialize it into a Component class with this code:
public Component getComponentMeasures(String componentKey, List<String> measures) throws ClientProtocolException,
IOException, JsonSyntaxException, UnsupportedOperationException, JSONException
{
HttpGet request = new HttpGet(baseURL + String.format("/api/measures/component?componentKey=%s&metricKeys=%s",
componentKey, StringUtils.join(measures, ",")));
HttpResponse response = client.execute(request);
Gson gson = new Gson();
String componenta = getJSONResponse(response);
System.out.print(componenta);
Component component = gson.fromJson(componenta, Component.class);
return component;
}
This is the Component class that I'm deserializing it into:
public class Component {
#SerializedName("id")
#Expose
private String id;
#SerializedName("key")
#Expose
private String key;
#SerializedName("name")
#Expose
private String name;
#SerializedName("qualifier")
#Expose
private String qualifier;
#SerializedName("path")
#Expose
private String path;
#SerializedName("measures")
#Expose
private Measure[] measures = null;
public String getId() {
return id;
}
public String getKey() {
return key;
}
public String getName() {
return name;
}
public String getQualifier() {
return qualifier;
}
public String getPath() {
return path;
}
public Measure[] getMeasures() {
return measures;
}
}
This Component class also contains an array of Measures which in turn contain an array of periods.
Measure Class:
public class Measure {
#SerializedName("metric")
#Expose
private String metric;
#SerializedName("value")
#Expose
private String value;
#SerializedName("periods")
#Expose
private Period[] periods = null;
public String getMetric() {
return metric;
}
public String getValue() {
return value;
}
public Period[] getPeriods() {
return periods;
}
}
Period class:
public class Period {
#SerializedName("index")
#Expose
private Integer index;
#SerializedName("value")
#Expose
private String value;
public Integer getIndex() {
return index;
}
public String getValue() {
return value;
}
}
When I run this code, the deserialized component is null. Any ideas on anything that I may be doing wrong here? Note that there is an extra parameter in the Component class, "path", that is null in the JSON. This is optional and exists in other classes which contain a collection of Component objects. In those cases, this Component object and JSON deserialize fine. I've compared the JSON side-by-side and they are identical. I only seem to have the issue when trying to deserialize a standalone component object. Any help would be greatly appreciated!
Note that your JSON document is a JSON object with a single property (the path: $.component) with a nested component, however you're trying to deserialize it as if it were the top-most object:
Component component = gson.fromJson(componenta, Component.class);
Just create another class to match the top-most single property object, say something like:
final class Response {
#SerializedName("component")
#Expose
final Component component = null;
}
And then sample code like
final Response response = gson.fromJson(componenta, Response.class);
for ( final Measure measure : response.component.measures ) {
System.out.println(measure.metric + " " + measure.value);
}
will print the following output:
coverage 19.9
overall_coverage 55.7
ncloc 1089127
Sonar has an SDK for their API that encapsulates all of this and you don't have to create your own classes, etc. I used it to get info out like you're doing. See https://docs.sonarqube.org/display/SONARQUBE45/Using+the+Web+Service+Java+client and my use of it: http://myvogonpoetry.com/wp/2013/02/21/using-the-sonar-rest-api-for-weekly-emails/

Categories

Resources