I have created customer json file as below:
[
{
"firstName": “test”,
"lastName": “temp”,
"age": 35,
"emailAddress": “test#Gmail.com",
"address": {
"streetAddress": “test testing“,
"city": “city”,
"postCode": “12343546”,
"state": “state”,
"country": “cy”,
"county": “abc”
},
"phoneNumber": {
"home": "012345678",
"mob": "0987654321"
}
},
{
"firstName": “tug”,
"lastName": “kjk”,
"age": 35,
"emailAddress": “jhgj#Gmail.com",
"address": {
"streetAddress": “jh hjgjhg ,
"city": “kjhjh”,
"postCode": "122345",
"state": “jhgl”,
"country": “jaj”,
"county": “jhgkg”
},
"phoneNumber": {
"home": "012345678",
"mob": "0987654321"
}
}
]
For the Customer JSON data file, I have created below JSON datareader class:
public class JsonDataReader {
private final String customerFilePath = new ConfigFileReader().getTestDataResourcePath() + "Customer.json";
private List<Customer> customerList;
public JsonDataReader(){
customerList = getCustomerData();
}
private List<Customer> getCustomerData() {
Gson gson = new Gson();
BufferedReader bufferReader = null;
try {
bufferReader = new BufferedReader(new FileReader(customerFilePath));
Customer[] customers = gson.fromJson(bufferReader, Customer[].class);
return Arrays.asList(customers);
}catch(FileNotFoundException e) {
throw new RuntimeException("Json file not found at path : " + customerFilePath);
}finally {
try { if(bufferReader != null) bufferReader.close();}
catch (IOException ignore) {}
}
}
public final Customer getCustomerByName(String customerName){
for(Customer customer : customerList) {
if(customer.firstName.equalsIgnoreCase(customerName)) return customer;
}
return null;
}
}
Created POJO class as below:
public class Customer {
public String firstName;
public String lastName;
public int age;
public String emailAddress;
public Address address;
public PhoneNumber phoneNumber;
public class Address {
public String streetAddress;
public String city;
public String postCode;
public String state;
public String country;
public String county;
}
public class PhoneNumber {
public String home;
public String mob;
}
}
This is working fine so far as there is only one JSON data file, however I will create more JSON data files, so may be I have to create multiple POJOs for each one, but is there any way I can write common generic jsondatareader class for all those JSON files?
A class (or an Object) is a well defined entity. By well defined I mean that its structure is known at compile time, and cannot be changed after that point.
Having to create multiple classes to represent multiple JSON documents is perfectly fine. So if you're worried about the amount of files you'll create, it's a non-problem.
But, if the JSON document structure will keep changing along with every request, there is no point in defining a series of classes. To handle totally dynamic JSON you should stick with what Gson offers you. That is JsonElement and its subclasses.
JsonElement
> JsonArray
> JsonObject
> JsonPrimitive
> JsonNull
That's all what is needed to describe a JSON object.
If that is the case then why not convert JSON into a Map instead of a POJO! If you go POJO route then you will utilizing Jackson or GSon heavily in your code base adding bunch of utility methods to iterate over every resulting JSonArray or JSonelements.
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
I am trying to convert JSON objects to POJO's with GSON.
JSON String
[
{
"automation_project": {
"user_id": null,
"name": "Untitled Project",
"updated_at": "2015-06-16T19:39:42Z",
"group_id": 764496,
"created_at": "2014-11-23T01:01:59Z",
"id": 16214
}
},
{
"automation_project": {
"user_id": null,
"name": "newintropage",
"updated_at": "2015-06-16T21:20:47Z",
"group_id": 764496,
"created_at": "2015-06-16T20:39:04Z",
"id": 29501
}
}
]
The AutomationProjectsList class used with GSON
public class AutomationProjectsList {
private List<AutomationProject> automationProject = new ArrayList<AutomationProject>();
public List<AutomationProject> getAutomationProject() {
return automationProject;
}
public void setAutomationProject(List<AutomationProject> automationProject) {
this.automationProject = automationProject;
}
#Override
public String toString() {
return "AutomationProjectsList [automationProject=" + automationProject
+ "]";
}}
Automation Project POJO
public class AutomationProject {
private Object userId;
private Integer groupId;
private Integer id;
private String name;
private String updatedAt;
private String createdAt;
public Object getUserId() {
return userId;
}
public void setUserId(Object userId) {
this.userId = userId;
}
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}}
The code I'm using
JSONArray jsonArray = new JSONArray(response.getEntity(String.class));
for(int i = 0; i < jsonArray.length(); i++){
if(jsonArray.get(i) instanceof JSONObject){
JSONObject jsnObj = (JSONObject)jsonArray.get(i);
AutomationProjectsList obj = new Gson().fromJson(jsnObj.toString(), AutomationProjectsList.class);
System.out.println(obj.getAutomationProject().get(0).getId());
}
}
But it gives an exception :
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at br.usp.icmc.teste.ConnectionRestClient.getBrowserStackProjects(ConnectionRestClient.java:74)
at br.usp.icmc.teste.TestePrincipal.main(TestePrincipal.java:9)
Why am I receiving an IndexOutOfBoundsException exception? Where am I wrong?
Your class or your JSON are incorrect. I'd suggest your JSON is.
A JSON matching your POJO class would be:
{
"automationProjects":[
{
"user_id": null,
"name": "Untitled Project",
"updated_at": "2015-06-16T19:39:42Z",
"group_id": 764496,
"created_at": "2014-11-23T01:01:59Z",
"id": 16214
},
{
"user_id": null,
"name": "newintropage",
"updated_at": "2015-06-16T21:20:47Z",
"group_id": 764496,
"created_at": "2015-06-16T20:39:04Z",
"id": 29501
}
]
}
Notice I used the name automationProjects for the list as it makes more sense, so your class would be:
public class AutomationProjectsList {
private List<AutomationProject> automationProjects = new ArrayList<AutomationProject>();
public List<AutomationProject> getAutomationProjects() {
return automationProjects;
}
public void setAutomationProjects(List<AutomationProject> automationProjects) {
this.automationProjects = automationProjects;
}
#Override
public String toString() {
return "AutomationProjectsList [automationProject=" + automationProject
+ "]";
}
}
And finally to convert JSON to AutomationProjectsList object:
AutomationProjectsList projectsList = new Gson().fromJson(jsonArray.toString(), AutomationProjectsList.class);
Then if you want to log each project:
for(AutomationProject project : projectsList.automationProjects){
System.out.println(porject.getId());
}
In conclusion, your code seems to have the fallowing issues:
Do you have a list of lists or just a single list of projects? If the list is just one, why do you iterate jsonArray like its sub-objects are lists themselves?
If you model your class correctly on the JSON then you don't need to iterate the JSON to obtain your objects
The JSON you posted is quite weird and uneasy to use with Gson, is it a requirement or can you edit it as you please?
Hope this helps
EDIT
Since you stated you cannot change the JSON you get, then it gets a little more complex, but everything is up to modelling the classes on the JSON format. So let's start form this JSON:
[
{
"automation_project": {
"user_id": null,
"name": "Untitled Project",
"updated_at": "2015-06-16T19:39:42Z",
"group_id": 764496,
"created_at": "2014-11-23T01:01:59Z",
"id": 16214
}
},
{
"automation_project": {
"user_id": null,
"name": "newintropage",
"updated_at": "2015-06-16T21:20:47Z",
"group_id": 764496,
"created_at": "2015-06-16T20:39:04Z",
"id": 29501
}
}
]
Now, this is quite nasty, but let's see what we have here: we have an unnamed array of objects with a single attribute "automationProject" which is our actual AutomationProject Object. So in terms of structure, it is a list of objects which wrap an actual AutomationProject.
Thus you'll need to get rid of your AutomationProjectList and change it with the more meaningful AutomationProjectWrapper looking as fallows:
public class AutomationProjectsWrapper {
private AutomationProject automation_project = new AutomationProject();
public AutomationProject getAutomationProject() {
return automation_project;
}
public void setAutomationProject(AutomationProject automationProject) {
this.automation_project = automationProject;
}
#Override
public String toString() {
return "AutomationProjectsList [automationProject=" + automation_project
+ "]";
}
}
See this class is equivalent to the JSON Object:
{
"automation_project": {
"user_id": null,
"name": "Untitled Project",
"updated_at": "2015-06-16T19:39:42Z",
"group_id": 764496,
"created_at": "2014-11-23T01:01:59Z",
"id": 16214
}
}
Finally you'll have an array of such wrapper objects as your jsonArray so you can write:
AutomationProjectWrapper[] projectsList = new Gson().fromJson(jsonArray.toString(), AutomationProjectWrapper[].class);
Then to log your objects:
for(AutomationProjectWrapper wrapper : projectsList){
System.out.println(wrapper.getAutomationProject().getId());
}
EDIT 2
Sorry for the mistake, in AutomationProjectWrapper class the AutomationProject field should be named automation_project.
Fixed in code above.
According to your JSON String the value you are trying to access is :
jsonString[i].automation_project.user_id
In your code you have: obj.getAutomationProject().get(0).getId()
I think is should be: obj[i].getAutomationProject().getId()