Deserializing objects with nested ArrayLists with Gson? - java

So I'm having some trouble with deserializing some JSON that has nested ArrayLists the root element deserializes okay but the nested ArrayList inside of it (Called mTest) are of null value. First here is the valid JSON.
[
{
"mRecipeName": "FirstRecipe",
"mTest": [
{
"mIngredientName": "TestIngredient1",
"mIngredientAmount": "1",
"mUnit": "tbsp"
},
{
"mIngredientName": "TestIngredient2",
"mIngredientAmount": "2",
"mUnit": "tbsp"
}
],
"mRecipeDescription": "Recipe1"
},
{
"mRecipeName": "SecondRecipe",
"mTest": [
{
"mIngredientName": "TestIngredient1",
"mIngredientAmount": "1",
"mUnit": "tbsp"
},
{
"mIngredientName": "TestIngredient2",
"mIngredientAmount": "2",
"mUnit": "tbsp"
}
],
"mRecipeDescription": "Recipe2"
}
]
Here is the Recipe Class
public class Recipe {
public String mRecipeName; //This values good
public String mRecipeDescription; //This values good
public ArrayList<Test> mTest; //This returns null
}
Here is the Test Class
public class Test {
public String mIngredientName;
public float mIngredientAmount;
public String mUnit;
}
And here is how I'm calling it
//Is there another way to do this? Or does this have to be done for every nested
//ArrayList? If so how?
Gson gson = new Gson();
ArrayList<Recipe> result = gson.fromJson(json, new TypeToken<ArrayList<Recipe>>() {}.getType());
Thank you very much in advance to anyone who replies!

So after another hour of hair tearing I realized that "Test" was coming from the JUnit framework and not my own test class, oh the agony! However, what led me to make a test class in the first place was the fact that it wasn't working and of course mysteriously now it is.

Related

How to return a list of multiple JSON items in java

I am trying to figure out how to return multiple JSON items. Right now I am able, to return a single JSON like so:
{
"result": {
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
"error": null
}
But I would like to return multiple JSON items, like so:
{
"result": {{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
}
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
},
"error": null
}
I can view the multiple JSON items as string, like below, but I would like to return it as multiple JSON items, but I don't know how:
[LDAPModel(userId=abcde123, telephoneNumber=1-555-5555555), LDAPModel(userId=fghi456, telephoneNumber=1-333-3333333]
I am a complete beginner in Java, and I don't know the syntax or much in Java. But I was given these codes (including the one below) from SpringBoot; I really don't understand what it is doing, and so I have no idea how create an output of list.
Currently, this is what I was given:
public Optional<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
// for (int ii = 0; ii < ldapModelList.size(); ii++) {
// Optional.of(ldapModelList.get(ii));
// ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(ii));
// }
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(0));
}
I tried putting it in a loop (like in the commented out code above), but I don't know how create a list. I tried removing the get(0), but there was a syntax error... There are many things I tried, but it just did not help.
Anyone can help?
Update/Edit: Thank you all for your answers. I posted a follow up question here. If you have a chance, please help me out. Thanks.
First of all I would like to point out that your JSON isn't formatted properly. When you want to represent multiple objects in JSON you should use square brackets and separate each object with a comma:
{
"result": [
{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
],
"error": null
}
The reason your Java code does not work when you try and remove get(0) is because the method public Optional<LDAPModel> getDirectReports(String cdsID) returns an Optional<LDAPModel> type and by removing get(0) your are effectively trying to return an Optional<List<LDAPModel>>. If you want the method to return a list instead of a single object you can change the return type to Optional<List<LDAPModel>> and then safely remove get(0).
public Optional<List<LDAPModel>> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList);
}
The structure looks strange to me. What you have looks like you want result to be an array of objects:
{
"result": [
{ "userId": "abcde123",
"telephoneNumber": "1-555-5555555" }
{ "userId": "fghi456",
"telephoneNumber": "1-333-3333333" }
],
"error": null
}
Given a reasonable JSON library, then the value of the "result" member of the JSON object is a JSON array, from which you can then pick out each element in turn by indexing, and each element is a JSON object with 2 members.
I assume you already managed to get all the list/array of LDAPModel i.e. List ldapModelList
If so, you just need to return this ldapModelList in your getDirectReports method.
public List<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList;
}
Then just use your library to return the json array. I suppose you use jackson.
Just make sure in LDAPModel you have
getters and setters
empty constructor if you add your own constructor having params. But if you don't add any constructor, then no need to add this default empty constructor as java will automatically create it for you.
LDAPModel class is as follows:
public class LDAPModel {
String userId;
String telephoneNumber;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setTelephoneNumber(String telephoneNumber) {
this.telephoneNumber = telephoneNumber;
}
}
For the object to JSON string conversion using Jackson, I assume you already know it or can find out how.

JSON array parsing with different types

I am using vk.com API and in one method in response I am getting something like this:
{
"ts": 1691519416,
"updates": [
[
6,
2000000024,
586731
],
[
4,
586732,
8243,
2000000024,
1512642885,
"income message",
{
"from": "384574802"
}
]
]
}
The problem is I am using Gson and I don't know what type of array I need to use.
For now I have this:
public class Updates {
public int ts;
public Update[] updates;
}
I don`t know what to put inside/instead of updates array.
Found a solution, thank you guys for answers. I just needed to use generics and a 2 dimensional array. The code of Updates class:
public class Updates {
public int ts;
public <?>[][] updates;
}
You can create your class like:
class Response
{
Timestamp ts;
Updates[] updates;
}
And use GSON:
Response response = gson.fromJson(jsonString, Response.class);
Just need to create a generic array.
private Object<?>[] json;

Deserialize an array of objects with Gson

I know how to deserialize normal JSON object with "Gson" library but I am facing problem to deserialize an JSON array with several JSON object and arrays. I am trying to get the time in the arrival_time JSON object in this simple below but I don't know how to structure my class to accomplish that. Can someone explain me how to do that?
Simple:
[{"route": 1,
"info": [
{"direction": "Surrey Quays"},
{"stops": [{"stops_name": " Tenison Way"},
{"arrival_time":{
"mon-fri": [ "05:38", "06:07","06:37"],
"sat": ["05:34","06:01","06:31"],
"son": ["06:02","06:34","07:04"]
}
}
]
}
]
}]
You can parse this Json using following structure:
class ArrivalTime {
public List<String> mon_fri;
public List<String> sat;
public List<String> son;
}
class Stop {
public String stop_name;
public ArrivalTime arrival_time;
}
class Info {
public String direction;
public List<Stop> stops;
}
class RouteInfo {
public Integer route;
public List<Info> info;
}
and then use it like this:
Gson gson = new Gson();
RouteInfo[] routes = gson.fromJson(/* your json string*/, RouteInfo[].class);
Arrival times will be available at something like this (it is ugly but I just want you to present the sample structure for this json string):
System.out.println(routes[0].info.get(1).stops.get(1).arrival_time.sat.get(0));
To learn the structure you could use a javascript object or a online builder.
http://www.jsonschema2pojo.org/

Using GSON to parse json object vs json array

I have a JSON that is either a single object or an array of the same object. Is there a way to parse this data using Gson where it'll distinguish between the single object vs the array?
The only solution I currently have for this is to manually parse the json and surround that with a try catch. First I'll try parsing it as a single object, if it fails, it'll throw an exception and then I'll try to parse it as an array.
I don't want to parse it manually though...that would take me forever.
Here's an idea of what's happening.
public class ObjectA implements Serializable{
public String variable;
public ObjectB[] objectb; //or ObjectB objectb;
public ObjectA (){}
}
Here's the object that can either be an array or a single object.
public class ObjectB implements Serializable{
public String variable1;
public String variable2;
public ObjectB (){}
}
And then when interacting with the json response. I'm doing this.
Gson gson = new Gson();
ObjectA[] objectList = gson.fromJson(response, ObjectA[].class);
When the array of ObjectA's are being serialized, the json contains either an array or single object for ObjectB.
[
{
"variable": "blah blah",
"objectb": {
"variable1": "1",
"variable2": "2"
}
},
{
"variable": "blah blah",
"objectb": {
"variable1": "1",
"variable2": "2"
}
},
{
"variable": "blah blah",
"objectb": [
{
"variable1": "1",
"variable2": "2"
},
{
"variable1": "1",
"variable2": "2"
}
]
}
]
I just changed ObjectB[] to List<ObjectB> into ObjectA declaration.
ArrayList<ObjectA> la = new ArrayList<ObjectA>();
List<ObjectA> list = new Gson().fromJson(json, la.getClass());
for (Object a : list)
{
System.out.println(a);
}
and this is my result:
{variable=blah blah, objectb={variable1=1, variable2=2}}
{variable=blah blah, objectb={variable1=1, variable2=2}}
{variable=blah blah, objectb=[{variable1=1, variable2=2}, {variable1=1, variable2=2}]}
I think that in full generics era, if you do not have particular needs, you can switch from arrays to lists, you have many benefits that Gson also can use to do a flexible parsing.
Try to use com.google.gson.JsonParser.
String jsonString = "object json representation";
JsonParser jsonParser = new JsonParser();
JsonElement jsonElement = jsonParser.parse(jsonString);
if (jsonElement.isJsonArray()) {
// some logic
}
There are different ways to get your object using the JsonElement instance, for example - simply using the com.google.gson.Gson methods :
public <T> T fromJson(com.google.gson.JsonElement json, java.lang.Class<T> classOfT) throws com.google.gson.JsonSyntaxException
public <T> T fromJson(com.google.gson.JsonElement json, java.lang.reflect.Type typeOfT) throws com.google.gson.JsonSyntaxException
So, study the JsonElement, JsonArray, JsonPrimitive, JsonNull and JsonObject classes. Believe, they have an adequate interface to recover your object.
Can you try/catch it by first trying to parse the array, then falling back to parsing the single object class?
You could also do a real simple test and look to the first non whitespace character in the string you are deseralizing, if it is a "{" it is a single object, if it is a "[" it is an array

Problem with beans and Jackson library

HI!
I am working with a .json file, like this:
[{
"SourceFile": "videos/KobeAlleyOop.flv",
"ExifTool": {
"ExifToolVersion": 8.22,
"Warning": "Truncated 'mdat' data"
},
"System": {
"FileName": "KobeAlleyOop.flv",
"Directory": "videos",
"FileSize": "4.8 MB",
"FileModifyDate": "2010:06:15 14:57:24+02:00",
"FilePermissions": "rwxr-xr-x"
},
"File": {
"FileType": "MP4",
"MIMEType": "video/mp4"
}]
I made a Bean with 3 components:
public class MetadataContentBean {
SourceFileBean sourceFileBean;
FileBean fileBean;
SystemBean systemBean;
public FileBean getFileBean() { return fileBean; }
#JsonProperty("File")
public void setFileBean(FileBean fileBean) {
this.fileBean = fileBean; }
public SystemBean getSystemBean() {
return systemBean; }
#JsonProperty("System")
public void setSystemBean(SystemBean systemBean) {
this.systemBean = systemBean; }
public SourceFileBean
getSourceFileBean() {
sourceFileBean.getSource(); return
sourceFileBean; }
#JsonProperty("SourceFile")
public void setSourceFileBean(SourceFileBean
sourceFileBean) {
this.sourceFileBean = sourceFileBean;
} }
And I add an example of SourceFileBean, the others are similar:
public class SourceFileBean {
private String source;
public String getSource() {
return source;
}
#JsonProperty("SourceFile")
public void setSource(String source) {
this.source = source;
}
}
In the main program I make this call:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(filename);
String jsonTxt = IOUtils.toString(is);
JSONArray json = (JSONArray) JSONSerializer.toJSON(jsonTxt);
JSONObject metadatacontent = json.getJSONObject(0);
ObjectMapper mapper = new ObjectMapper(); mapper.readValue(metadatacontent.toString(),MetadataContentBean.class);
But I get this error when I run it, I don't know why:
org.codehaus.jackson.map.JsonMappingException:
Can not construct instance of
com.path.bean.SourceFileBean,
problem: no suitable creator method
found at [Source:
java.io.StringReader#12d7a10; line: 1,
column: 2] at
org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:159)
at
org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:212)
at
org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromString(BeanDeserializer.java:415)
at
org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:291)
at
org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:135)
at
org.codehaus.jackson.map.deser.SettableBeanProperty$MethodProperty.deserializeAndSet(SettableBeanProperty.java:221)
at
org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:390)
at
org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:286)
at
org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:1588)
at
org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1116)
at
com.path.parser.JSon.Parser(JSon.java:65)
at
com.path.parser.JSon.main(JSon.java:29)
Any help?? Thanks in advance!
I'm guessing that this is just because your JSON represents an array, with a single object inside it. You're asking Jackson to deserialize this array data onto a single instance of MetadataContentBean, which it can't do.
Try removing the [] brackets from around the JSOn, and try again.
The problem was about sintaxis and the way of writting the fields in my program.
You must be absotuely sure that it is the SAME as in the json file.
On the other hand
"SourceFile": "videos/KobeAlleyOop.flv"
is a field with just one field, so is not neccesary make a bean for it.
It is a stupid error which could make you waist a lot of time!!! :s
One problem is that you have unnecessary code in there: lines 3 and 4 are not needed and could cause issues. So just do:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(filename);
String jsonTxt = IOUtils.toString(is);
ObjectMapper mapper = new ObjectMapper();
MetadataContentBean[] beans = mapper.readValue(metadatacontent.toString(),MetadataContentBean[].class);
so you don't have to use json.org's parser in there. This may not explain exact problem but helps avoid secondary issues.
But the specific problem that throws exception is simple(r): JSON value for type is String, but you are trying to make an Object (bean) out of it.
To make it work, add a public constructor that takes one String argument, and it should work.
You can annotate it with #JsonCreator if you want (or if it's not public constructor), but that should not be necessary.
Conversely, if you want to serialize a bean as JSON String, you need to do something like
#JsonValue public String asString() { return valueOfThisAsString; }

Categories