Retrofit : How to decide the class members? - java

I've been trying retrofit for Android. The response has been null. If my understanding is correct, this might be because of a 400 response or an incorrect modelling of the response in my model class. The response that I am getting is as follows:
{"itemA":"data",
"itemB":"data",
"itemC":"data",
"ItemC":"",
"result_arr":[{"Val1":"A","Val2":"","id":"id","pr":"$0.00","sid":"a","cid":"a","price":"$0.00","cool_down":"0%","url":"","name":"Name"},
{"Val1":"A","Val2":"","id":"id","pr":"$0.00","sid":"a","cid":"a","price":"$0.00","cool_down":"0%","url":"","name":"Name"}]
,"statusCode":"200"}
The models that I have defined are as follows:
API result
public class APIResultModel {
#SerializedName("itemA")
public String itemA;
#SerializedName("itemB")
public String itemB;
#SerializedName("itemC")
public String itemC;
#SerializedName("itemD")
public string itemD;
#SerializedName("results_arr")
public List<ProductModel> results_arr;
#SerializedName("status_code")
public String statusCode;
}
Result Array Model:
public class ResultArrayModel {
public String val1;
public String val2;
public String id;
public String pr;
public String sid;
public String cid;
public String price;
public String cool_down;
public String url;
public String name;
}
How should a model for this response look like? And how is the model derived from the response values?

Looking at your code you seem to be using Gson.
In order for Gson to create your pojo, your model's serializedNames have to match the json response you're getting.
You'll have to change:
#SerializedName("status_code")
to:
#SerializedName("statusCode")
Make sure all your attributes are following this rule and you're good to go.

Given the JSON:
{
"itemA": "data",
"itemB": "data",
"itemC": "data",
"ItemD": "",
"result_arr": [
{
"Val1": "A",
"Val2": "",
"id": "id",
"pr": "$0.00",
"sid": "a",
"cid": "a",
"price": "$0.00",
"cool_down": "0%",
"url": "",
"name": "Name"
},
{
"Val1": "A",
"Val2": "",
"id": "id",
"pr": "$0.00",
"sid": "a",
"cid": "a",
"price": "$0.00",
"cool_down": "0%",
"url": "",
"name": "Name"
}
],
"statusCode": "200"
}
Your api result model may be:
public class APIResult {
public String itemA;
public String itemB;
public String itemC;
public String itemD;
#SerializedName("results_arr")
public List<Product> products;
public String statusCode;
}
And your product model may be:
public class Product {
#SerializedName("Val1")
public String val1;
#SerializedName("Val2")
public String val2;
public String id;
public String pr;
public String sid;
public String cid;
public String price;
#SerializedName("cool_down")
public String coolDown;
public String url;
public String name;
}
Supposing you are using GSON, you should only use the SerializedName annotation when the field name is not the same as the present in JSON.
There are some applications that do a conversion from JSON to POJO, Tyr for example.

Related

Insert Data to MongoDb Using Panache, Quarkus

I am trying to add data into a mongodb collection using Panache and Quarkus. I am trying to insert a nested document that is like the one below
{
"user_id": 8001,
"name": "John John",
"email":"jj#justiceleague.com",
"entity":6,
"business_unit": 3,
"contact_person":"Bats Nopower",
"contact_person_phone":"+25472000001",
"verification_data":{
"national_id": "987643",
"tax_pin":"A0GYE09753ew",
"driving_licence":"6473412"
},
"country":"KE"
}
However when I insert the data this is how it looks like
{
"_id": {
"$oid": "609472b1b410cd46bc3bc674"
},
"business_unit": 3,
"country": "KE",
"created_date": {
"$date": "2021-05-07T01:50:25.341Z"
},
"email": "jj#justiceleague.com",
"entity": 6,
"name": "John John",
"user_id": 8001,
"verification_data": {
"verification_data": {
"national_id": "987643",
"tax_pin": "A0GYE09753ew",
"driving_licence": "6473412"
}
}
}
verification_data is nested inside another verification_data. Not sure what am missing
This is how my data class looks like
#MongoEntity(collection="basic_info")
public class BasicInfo extends PanacheMongoEntity {
#SerializedName("user_id")
#BsonProperty("user_id")
public Integer userId;
public String name;
public String email;
public Integer entity;
#BsonProperty("business_unit")
public Integer businessUnit;
#BsonProperty("contact_person")
public String contactPerson;
#BsonProperty("contact_person_phone")
public String contactPhone;
#BsonProperty("created_date")
public LocalDateTime createdDate;
public String country;
#BsonProperty("verification_data")
public Object verificationData;
//getters and setters
}
This is how I persist the data
public void addBasicInfo(BasicInfo basicInfo) {
BasicInfo initialData = new BasicInfo();
initialData.setUserId(basicInfo.getUserId());
initialData.setName(basicInfo.getName());
initialData.setEmail(basicInfo.getEmail());
initialData.setEntity(basicInfo.getEntity());
initialData.setBusinessUnit(basicInfo.getBusinessUnit());
initialData.setVerificationData(basicInfo.getVerificationData());
initialData.setCountry(basicInfo.getCountry());
initialData.setCreatedDate(LocalDateTime.now());
initialData.persist();
}
This is how my codec looks like
public class ObjectCodec implements Codec<Object> {
private final Codec<Document> documentCodec;
public ObjectCodec() {
this.documentCodec = MongoClientSettings.getDefaultCodecRegistry().get(Document.class);
}
#Override
public void encode(BsonWriter writer, Object object, EncoderContext encoderContext) {
Document doc = new Document();
doc.put("verification_data", object);
documentCodec.encode(writer, doc, encoderContext);
}
#Override
public Class<Object> getEncoderClass() {
return Object.class;
}
//...
}
Note: I do not know how verification_data will look like before hand that is why am treating it as an Object.

JSON Array of objects to plain old java object

this is my first time making an external api call in Java, so please bear with me as I'm not very experienced. I got the http request working and got a response, but now I need to parse it.
I'm trying to convert a json array to java objects. I understand the gist of it, but all examples I've seen don't apply to my issue.
I need the 'entities' objects from the json string. The details (which are an array, too) can contain any key/value pair, so I was thinking of putting that in a hashmap in each Entity object. I've tried the gson library, but I can't find any gson example that goes deeper than a single dimensional json array.
I realize this is kind of a broad question, and I don't expect anyone to deliver me a working solution, but a few tips or a link to a relevant guide would go a long way. :)
{
"return": {
"entities": [
{
"id": 2385,
"details": [
{
"name": "Other Known Name",
"value": "John Wick",
"match": false
}
],
"proofs": [],
"link": "http://domain.gg/users?id=2385"
},
{
"id": 2384,
"details": [
{
"name": "Discord ID",
"value": "159985870458322944",
"match": false
},
{
"name": "SteamID64",
"value": "76561197991558078",
"match": true
},
{
"name": "SteamVanity",
"value": "test",
"match": false
},
{
"name": "PS4",
"value": "John_S",
"match": false
},
{
"name": "XBox",
"value": "John S",
"match": false
},
{
"name": "Email",
"value": "john_smith#gmail.com",
"match": true
},
{
"name": "Comment",
"value": "Test user",
"match": false
},
{
"name": "Other Known Name",
"value": "Jonathan",
"match": false
},
{
"name": "Reddit",
"value": "/u/johns",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=2384"
},
{
"id": 1680,
"details": [
{
"name": "Other Known Name",
"value": "Johny",
"match": false
},
{
"name": "SteamID64",
"value": "76561198213003675",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=1680"
},
{
"id": 1689,
"details": [
{
"name": "Other Known Name",
"value": "JohnnyPeto",
"match": false
},
{
"name": "SteamID64",
"value": "76561198094228192",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=1689"
}
],
"notice": "Showing 4 out of 4 matches."
}
}
There are many json serialization/deserialization frameworks available. I would recommend having a look at Jackson.
Basically, you have to create Model corresponding to json schema and deserialize json into object. Based on the example in the question, model will look like this:
#JsonIgnoreProperties(ignoreUnknown = true)
class Response {
#JsonProperty("return")
private ResponseObject responseObject;
public ResponseObject getResponseObject() {
return responseObject;
}
public void setResponseObject(ResponseObject responseObject) {
this.responseObject = responseObject;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class ResponseObject {
private List<Entity> entities;
public List<Entity> getEntities() {
return entities;
}
public void setEntities(List<Entity> entities) {
this.entities = entities;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Entity {
private String id;
private List<Details> details;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Details> getDetails() {
return details;
}
public void setDetails(List<Details> details) {
this.details = details;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Details {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Once the model is defined, you can use ObjectMapper class to perform serialization/deserialization, e.g.:
ObjectMapper mapper = new ObjectMapper();
Response response = mapper.readValue("{\"return\": {\"entities\": [{\"id\": 2385,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"John Wick\",\"match\": false}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2385\"},{\"id\": 2384,\"details\": [{\"name\": \"Discord ID\",\"value\": \"159985870458322944\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561197991558078\",\"match\": true},{\"name\": \"SteamVanity\",\"value\": \"test\",\"match\": false},{\"name\": \"PS4\",\"value\": \"John_S\",\"match\": false},{\"name\": \"XBox\",\"value\": \"John S\",\"match\": false},{\"name\": \"Email\",\"value\": \"john_smith#gmail.com\",\"match\": true},{\"name\": \"Comment\",\"value\": \"Test user\",\"match\": false},{\"name\": \"Other Known Name\",\"value\": \"Jonathan\",\"match\": false},{\"name\": \"Reddit\",\"value\": \"/u/johns\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2384\"},{\"id\": 1680,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"Johny\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198213003675\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1680\"},{\"id\": 1689,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"JohnnyPeto\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198094228192\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1689\"}],\"notice\": \"Showing 4 out of 4 matches.\"}}", Response.class);
System.out.println(response.getResponseObject().getEntities().get(0).getId());
Here's the Javadoc.
If I were you, I'd use Jackson, not GSON. It's specialized on JavaBeans-style mapping. Write classes like this:
public class Detail{
private String name;
private String value;
private boolean match;
// + getters / setters
}
public class Entity{
private int id;
private List<Detail> details;
private String link;
private List<String> proofs;
// you don't have any example data for this, so I'm assuming strings
// + getters / setters
}
public class Result{
private List<Entity> entities;
private String notice;
// + getters / setters
}
and do the conversion with something like
Result result = new ObjectMapper().readValue(json, Result.class);
As my fellow stackoverflow users have previously posted, for this kind of initilization Jackson API would be better. I have however posted the solution for your question with Gson.
I noticed that you like your details to be stored as a HashMap with id as key. However, it seems like this id is actually related to the entities and not to the details.
Disclaimer, I got lazy and used an online POJO generator because I did not want to create objects for all of the Json elements ;) It still showcases how it should be done:
class Main{
public static void main(String[] args) throws FileNotFoundException {
//this is just to load the json file
String input = new Scanner(new File("test.txt")).useDelimiter("\\Z").next();
System.out.println(input);
Gson gson = new Gson();
Example arr = gson.fromJson(input, Example.class);
System.out.println(arr);
}
public class Detail {
#SerializedName("name")
#Expose
public String name;
#SerializedName("value")
#Expose
public String value;
#SerializedName("match")
#Expose
public Boolean match;
#Override
public String toString() {
return "Detail [name=" + name + ", value=" + value + ", match=" + match + "]";
}
}
public class Entity {
#SerializedName("id")
#Expose
public Integer id;
#SerializedName("details")
#Expose
public List<Detail> details = null;
#SerializedName("proofs")
#Expose
public List<Object> proofs = null;
#SerializedName("link")
#Expose
public String link;
#Override
public String toString() {
return "Entity [id=" + id + ", details=" + details + ", proofs=" + proofs + ", link=" + link + "]";
}
}
public class Example {
#SerializedName("return")
#Expose
public Return _return;
#Override
public String toString() {
return "Example [_return=" + _return + "]";
}
}
public class Return {
#SerializedName("entities")
#Expose
public List<Entity> entities = null;
#SerializedName("notice")
#Expose
public String notice;
#Override
public String toString() {
return "Return [entities=" + entities + ", notice=" + notice + "]";
}
}
}
Output
Example [_return=Return [entities=[Entity [id=2385, details=[Detail [name=Other Known Name, value=John Wick, match=false]], proofs=[], link=http://domain.gg/users?id=2385], Entity [id=2384, details=[Detail [name=Discord ID, value=159985870458322944, match=false], Detail [name=SteamID64, value=76561197991558078, match=true], Detail [name=SteamVanity, value=test, match=false], Detail [name=PS4, value=John_S, match=false], Detail [name=XBox, value=John S, match=false], Detail [name=Email, value=john_smith#gmail.com, match=true], Detail [name=Comment, value=Test user, match=false], Detail [name=Other Known Name, value=Jonathan, match=false], Detail [name=Reddit, value=/u/johns, match=true]], proofs=[], link=http://domain.gg/users?id=2384], Entity [id=1680, details=[Detail [name=Other Known Name, value=Johny, match=false], Detail [name=SteamID64, value=76561198213003675, match=true]], proofs=[], link=http://domain.gg/users?id=1680], Entity [id=1689, details=[Detail [name=Other Known Name, value=JohnnyPeto, match=false], Detail [name=SteamID64, value=76561198094228192, match=true]], proofs=[], link=http://domain.gg/users?id=1689]], notice=Showing 4 out of 4 matches.]]
Despite there are answers suggesting you to use Jackson, you can still accomplish easily with Gson with its default configuration just creating proper relations between mappings:
// A generic response, parameterized with <T>, can hold any type except of primitives
final class Response<T> {
#SerializedName("return")
final T ret = null;
}
final class EntitiesAndNotice {
final List<Entity> entities = null;
final String notice = null;
}
final class Entity {
// Unlike Object and any its subclasses, `int` being a primitive cannot be nulled
// Simple 0 won't work either, because the compiler will inline it
// So it's a sort of cheating javac to return a value that holds 0 already
final int id = Integer.valueOf(0);
final List<Detail> details = null;
// Your JSON document does not provide enough info on the elements type
// So it depends on how Gson parses JSON tokens
final List<Object> proofs = null;
final URL link = null;
}
final class Detail {
final String name = null;
final String value = null;
// The same for primitive booleans, or Boolean.FALSE
final boolean match = Boolean.valueOf(false);
}
Example use:
private static final String JSON = "{\"return\":{\"entities\":[{\"id\":2385,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"John Wick\",\"match\":false}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2385\"},{\"id\":2384,\"details\":[{\"name\":\"Discord ID\",\"value\":\"159985870458322944\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561197991558078\",\"match\":true},{\"name\":\"SteamVanity\",\"value\":\"test\",\"match\":false},{\"name\":\"PS4\",\"value\":\"John_S\",\"match\":false},{\"name\":\"XBox\",\"value\":\"John S\",\"match\":false},{\"name\":\"Email\",\"value\":\"john_smith#gmail.com\",\"match\":true},{\"name\":\"Comment\",\"value\":\"Test user\",\"match\":false},{\"name\":\"Other Known Name\",\"value\":\"Jonathan\",\"match\":false},{\"name\":\"Reddit\",\"value\":\"/u/johns\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2384\"},{\"id\":1680,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"Johny\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198213003675\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1680\"},{\"id\":1689,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"JohnnyPeto\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198094228192\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1689\"}],\"notice\":\"Showing 4 out of 4 matches.\"}}";
private static final Gson gson = new Gson();
private static final TypeToken<Response<EntitiesAndNotice>> responseTypeToken = new TypeToken<Response<EntitiesAndNotice>>() {
};
public static void main(final String... args) {
final Response<EntitiesAndNotice> response = gson.fromJson(JSON, responseTypeToken.getType());
final String value = response.ret.entities.get(1).details.get(3).value;
System.out.println(value);
}
Output:
John_S

Parsing two JSON files to POJOS, one references the other

I have two files: occupations.json and people.json. The former is just an array of occupations:
[
{ "name": "director", "pay": "100000"},
{ "name": "programmer", "pay": "75000"},
{ "name": "teacher", "pay": "50000"}
]
And the latter an array of a few people along with their occupation:
[
{ "name": "Mary", "occupation": "programmer" },
{ "name": "Jane", "occupation": "director" },
{ "name": "John", "occupation": "teacher" }
]
And these are the corresponding classes:
public class Occupation {
private final String name;
private final int pay;
public String getName() { ... }
public int getPay() { ... }
}
public class Person {
private final String name;
private final Occupation occupation;
public String getName() { ... }
public String getOccupation() { ... }
}
Currently I'm using ObjectMapper.readValue(InputStream, Class) to
unserialize the files. How can I make Person class aware of all existing Occupation objects? I want to select which occupation a person has by using the occupation's name.
add a function to Person.class
public Occupation getOccupation_()
{
// assume you had a static occupations list...
for (Occupation o : occupations)
if (o.name == this.occupation)
return o;
return null;
}

GSON parsing of nested array

Im having difficulties understanding if GSON can handle this kind of json by default or do I need to implement deserializers for every sub element.
json input
{
"services":[
{
"id": 2,
"name": "Buy"
},
{
"id": 3,
"name": "Sell"
}
]
"status": {
"code": 0,
"message": ""
}
}
The best case result on my part is to have the following class contain all the data
java [ POJO ]
public class Services {
public List<ServiceItem> services;
public Status status;
public class ServiceItem {
public int id;
public String name;
}
public class Status {
public int code;
public String message;
}
}
Is it possible to let GSON the class and the json and just let it work? Or do I need to create deserializers for each sub class?
Correct your json input as follow (you forgot a comma before status field)
{
"services":[
{
"id": 2,
"name": "Buy"
},
{
"id": 3,
"name": "Sell"
}
],
"status": {
"code": 0,
"message": ""
}
}
Then let's consider your classes as follow
public class Services {
public List<ServiceItem> services;
public Status status;
// getters and setters
#Override
public String toString() {
return "["+services.toString()+status.toString()+"]";
}
public class ServiceItem {
public int id;
public String name;
// getters and setters
#Override
public String toString() {
return "("+id+","+name+")";
}
}
public class Status {
public int code;
public String message;
// getters and setters
#Override
public String toString() {
return ",("+code+","+message+")";
}
}
}
If the input is a file jsonInput.json then
Gson gson = new Gson();
Services data = gson.fromJson(new BufferedReader(new FileReader(
"jsonInput.json")), new TypeToken<Services>() {
}.getType());
System.out.println(data);
If the input is a json String jsonInput then
Gson gson = new Gson();
Services data = gson.fromJson(jsonInput, Services.class);
System.out.println(data);
Output:
[[(2,Buy), (3,Sell)],(0,)]

JSONObject parse with gson

I want to parse my JSONObject with gson. I write some code but doesn't work.
public class User {
#SerializedName("Email")
public static String Email;
#SerializedName("Id")
public static int Id;
#SerializedName("Picture")
public static String Picture=null;
#SerializedName("UserName")
public static String UserName;
}
In my class:
User result=new Gson().fromJson(response,User.class);
My JSONObject:
{
"Email": "",
"Id": 1,
"Picture": null,
"UserName": "User1"
}
Remove static modifier from all variables and try it again.
Note: Make all variable private and provide public getter/setter methods. learn more...

Categories