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...