Populate POJO with array (root JSON node) using Jackson - java

I'm using Jackson and RESTEasy to hook into an external API. The API mainly returns simple objects which I have managed to successfully populate into POJOs.
I'm hitting a problem where I get an array of objects back e.g.
[
{
"variable1": "someValue1",
"variable2": "someValue2",
"variable3": "someValue3"
}
{
"variable1": "someValue4",
"variable2": "someValue5",
"variable3": "someValue6"
}
{
"variable1": "someValue7",
"variable2": "someValue8",
"variable3": "someValue9"
}
]
I have 2 classes: one called VariableObject which looks like this:
public class VariableObject {
private String variable1;
private String variable2;
private String variable3;
}
and VariableResponse which looks like:
public class VariableResponse {
private List<VariableObject> variableObjects;
}
My client uses JAXRS Response class to read the entity into the class i.e
return response.readEntity(VariableResponse.class);
I get a stack trace which reads:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of VariableResponse out of START_ARRAY token
I understand you can return these as a List of POJOs i.e List quite easily, but this is not what I want to do.
The question really is two parts:
a. Can I possibly populate the VariableResponse POJO using Jackson (some how) preferably without a customer deserialiser? Maybe some annotation exists (this would be ideal)?
b. Is there some way to detect if an Array is being retuned as the root JSON node in the response and then act accordingly?
Help greatly appreciated.

Your JSON is indeed an array of objects.
You can deserialize it with:
response.readEntity(new GenericType<List<VariableObject>>() {});
And then create a new instance of VariableResponse passing resulting List as a constructor parameter like this:
public class VariableResponse {
private final List<VariableObject> variableObjects;
public VariableResponse(List<VariableObject> variableObjects) {
this.variableObject = new ArrayList<>(variableObjects);
}
}

You might forget to add comma after each {..}. After correcting your JSON string, I converted it into ArrayList<VariableObject> using TypeReference and ObjectMapper.
sample code:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
...
TypeReference<ArrayList<VariableObject>> typeRef = new TypeReference<ArrayList<VariableObject>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
ArrayList<VariableObject> data = mapper.readValue(jsonString, typeRef);
for (VariableObject var: data) {
System.out.println(var.getVariable1()+","+var.getVariable2()+","+var.getVariable3());
}
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
output:
someValue1,someValue2,someValue3
someValue4,someValue5,someValue6
someValue7,someValue8,someValue9

If you prefer your own response type direct.
Try just extending ArrayList?
public VariableResponse extends ArrayList<VariableObject> {
}

Related

Is there a way to fetch Json values within Json?

I've got a Scenario Entity in my Spring boot app
private Long id;
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
Let's say we create a Scenario entity with following Json:
{
"scenarioName":"scenario1",
"scenarioAttributes":{
"1" : {
"goToURL":"https://google.com/"
},
"2" : {
"assertCurrentUrl":"https://google.com/"
}
}
}
In my ExecutorService, I've got following code:
public List<Object> getStepsFromScenarioAttributesValues(Long scenarioId){
List<Object> response = new ArrayList<>();
Scenario scenario = scenarioService.getScenario(scenarioId);
HashMap<Integer, Object> steps = scenario.getScenarioAttributes();
for (Map.Entry<Integer, Object> set : steps.entrySet()){
response.add(set.getValue());
System.out.println(response);
// prints out
//[{goToURL=https://google.com/}, {assertCurrentUrl=https://google.com/}]
}
return response;
}
public void executeSteps(List<Object> response){
for (Object obj : response){
JsonObject jsonObj = (JsonObject) obj;
if (jsonObj.has("goToUrl")) {
//goes to url
driverService.goToUrl(String.valueOf(jsonObj.get("goToUrl")));
return;
} else if (jsonObj.has("assertCurrentUrl")) {
//asserts cur url with value
driverService.assertCurrentUrl(String.valueOf(jsonObj.get("assertCurrentUrl")));
return;
}
}
}
public String executeScenario(Long scenarioId){
executeSteps(getStepsFromScenarioAttributesValues(scenarioId));
return "Scenario" + scenarioId + " has been executed successfully";
}
I've got a GetMapping for single scenario as follows:
#GetMapping("/scenarios/{id}/execute")
public List<Object> executeScenario(#PathVariable Long id){
return executorService.getStepsFromScenarioAttributesValues(id);
}
Which, upon sending one sample scenario and entering a site, provides us with, you guessed it, a List containing an Object, which is a Json.
Unfortunately, if I want to call executeSteps() function which has a list of Objects, I cannot do it since an Object is not a JsonObject.
I thought simple JsonObject jsonObj = (JsonObject) obj; would do a trick, but I'm greeted with
class java.util.LinkedHashMap cannot be cast to class com.google.gson.JsonObject (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.google.gson.JsonObject is in unnamed module of loader 'app')
How can I approach fetching values from scenarioAttributes Jsons to further interpret them?
Are there unnecessary lines of code that I could get rid of?
Feel free to point out my wrongdoings, just starting my journey with Spring
public class Scenario {
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
}
Use object mapper to print class object to JSON:
ObjectMapper objectMapper = new ObjectMapper();
Scenario scenario = objectMapper.readValue(response, Scenario.class);
When you say Object, which is a Json what do you mean by "Json"? Json is not a class. I assume that your Object is an instance of class String that contains text in Json format. Your exception though points out that it could be a Map. I would still assume that it is a String that holds Json. In your code you attempt to work with class JSONObject which is part of json-simple library. While it is a nice training library I would suggest that you don't use it. BTW great site about JSON with the list of most libraries for most languages is: https://www.json.org/json-en.html. Best options are (IMHO) are Jackson-Json (also known as Faster XML) or Gson (by Google) (Here is their user guide site). To parse your Json text to a class instance you can use ObjectMapper class which is part of Jackson-Json library. For example
public <T> T readValue(String content,
TypeReference valueTypeRef)
throws IOException,
JsonParseException,
JsonMappingException
See Javadoc. But I also may suggest a very simplified JsonUtils class which is a thin wrapper over Jackson-Json library. This class is part of Open-source library called MgntUtils written and maintained by me. Your code may be as simple as follows:
Scenario scenario;
try {
scenario = JsonUtils.readObjectFromJsonString(response, Scenario.class);
} catch(IOException ioe) {
....
}
Here is JsonUtils javadoc. MgntUtils library can be obtained as Maven artifact here or on Github (including source code and Javadoc)

How to use dynamic json value on my POJO class with Gson?

{
"localeCode": "",
"map": {
"DynamicName1": [],
"DynamicName2": [
{
"date": "2016-05-15T00:00:00",
"seqId": 1,
"status": 10
},
{
"date": "2016-05-16T00:00:00",
"seqId": 83,
"status": 10
}
],
"DynamicName3": [],
"DynamicName4": []
},
"respCode": 100,
"respMsg": "success",
"status": 1
}
How to correctly map this kind of json. If you can see that, Dynamic is a dynamic name. So far I have done this :
public class MapModel {
public MapObject map;
public static class MapObject{
public java.util.Map<String, Student> queryStudent;
public static class Student{
public String date;
public String seqId;
public String status;
}
}
}
But when run the app. I'm getting NullPointerException. Can somebody help me?
You're getting the NullPointerException accessing queryStudent of your MapObject inside your MapModel since it's not correctly filled when you're trying to deserialize your Json.
So to solve your problem look at Gson documentation where you can see:
You can serialize the collection with Gson without doing anything
specific: toJson(collection) would write out the desired output.
However, deserialization with fromJson(json, Collection.class) will
not work since Gson has no way of knowing how to map the input to the
types. Gson requires that you provide a genericised version of
collection type in fromJson(). So, you have three options:
Use Gson's parser API (low-level streaming parser or the DOM parser
JsonParser) to parse the array elements and then use Gson.fromJson()
on each of the array elements.This is the preferred approach. Here is
an example that demonstrates how to do this.
Register a type adapter for Collection.class that looks at each of the
array members and maps them to appropriate objects. The disadvantage
of this approach is that it will screw up deserialization of other
collection types in Gson.
Register a type adapter for MyCollectionMemberType and use fromJson()
with Collection.
Since your MapObject containts a java.util.Map but your class itself it's not generic, I think that a good approach for your case is create a Deserializer.
Before this try to clean up your class definition, to provide constructors to make the deserializer easy to build. Your POJO classes could be:
Student class
public class Student{
public String date;
public String seqId;
public String status;
public Student(String date, String seqId, String status){
this.date = date;
this.seqId = seqId;
this.status = status;
}
}
MapObject class
Note: I change you Map definition, since in your Json seems that could be multiple students for each DynamicName (look at DynamicName2 from your question), so I use Map<String,List<Student>> instead of Map<String,Student>:
public class MapObject{
public Map<String,List<Student>> queryStudent;
public MapObject(Map<String,List<Student>> value){
this.queryStudent = value;
}
}
MapModel class
public class MapModel {
public MapObject map;
}
Now create a Deserializer for your MapObject:
public class MapObjectDeserializer implements JsonDeserializer<MapObject> {
public MapObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Map<String,List<Student>> queryStudents = new HashMap<String,List<Student>>();
// for each DynamicElement...
for (Map.Entry<String,JsonElement> entry : json.getAsJsonObject().entrySet()) {
List<Student> students = new ArrayList<Student>();
// each dynamicElement has an Array so convert and add an student
// for each array entry
for(JsonElement elem : entry.getValue().getAsJsonArray()){
students.add(new Gson().fromJson(elem,Student.class));
}
// put the dinamic name and student on the map
queryStudents.put(entry.getKey(),students);
}
// finally create the mapObject
return new MapObject(queryStudents);
}
}
Finally register the Deserializer and parse your Json:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MapObject.class, new MapObjectDeserializer());
Gson gson = builder.create();
MapModel object = gson.fromJson(YourJson,MapModel.class);
DISCLAIMER: For fast prototyping I test this using groovy, I try to keep the Java syntax but I can forget something, anyway I think that this can put you on the right direction.
Hope it helps,

jackson in Spring MVC

I have two java classes for parsing jason into java alone. Beyond that, the classes are not used for any thing. Below are the two classes.
import java.util.ArrayList;
public class PaymentsPaid {
public ArrayList<PaidDetailAmounts> amount;
}
and
public class PaidDetailAmounts {
public Long invoiceFeeId;
public Double amountPaid;
}
Here is where the string and the use of and object mapper.
"amount": [{"invoiceFeeId": 12085, "amountPaid": 100},{"invoiceFeeId": 12084, "amountPaid": 100},{"invoiceFeeId": 12086, "amountPaid": 500}]
and the mapper code
ObjectMapper mapper = new ObjectMapper();
try {
PaymentsPaid paymentsPaidModel = mapper.readValue(httpServletRequest.getParameter("amount"), PaymentsPaid.class);
/*
Iterator<PaidDetailAmounts> iterator = paymentsPaidModel.amount.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().invoiceFeeId);
}
*/
} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
This is my exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "
invoiceFeeId" (Class PACKAGE_NAME.PaymentsPaid), not marked as ignorable
I must be doing something worng, because I built a search feature using this approach and it is currently in my application and working well. Please advise. I think it may be a mal formed json string, because it should be an array.
The Problem seams to be that jackson tryes to access the invoiceFeeId field of class PaymentsPaid, but it is a field of class PaidDetailAmounts.
I think there is some surrounding brackets missing in your Json string:
{ // <-- missing?
"amount": [{"invoiceFeeId":...}]
} // <-- missing?
But I am not an JSON expert, so I would try to write an simple test case that create a JSON string from some Java Objects and then parse this strings to back to Java Objects. So that the test can assert that both (sets of) objects are equals.
Then you can use the JSON String created by the test and compare it with your input, I would expect that the (missing) brackets are the difference between them.

"Dotting" in JSON using Gson on Android

I'm trying to parse a JSON feed using Gson in Android. I know the JSON is valid. I suspect that it is because the format is like this:
"Info":[
{
"Id":"",
"Name":"",
"Description":"",
"Date":""
}
In order to parse this I need to "dot" in. Ex: Info.Name
How can I do this in a serialized DTO?
#SerializedName("Name")
public String name;
#SerializedName("Description")
public String desc;
#SerializedName("Date")
public String date;
I tried to put "Info." in front of each serializedName but that didn't work either. I also know my JSON parsing method works properly, because it's used somewhere else with a different DTO. But in that parsing, I don't have to "dotting" issue.
Can anyone help?
EDIT: I have tried everything you guys posted, and nothing works. The error says:
The JsonDeserializer failed to deserialize json object {"Info":[{".......
SECOND EDIT:
I was able to get rid of the error, but now it returns null. Haha, getting pretty damn frustrated right about now!
I am assuming that the actual JSON you are intaking is valid because the example you provided is not. In your JSON example, you have "Info":[ but there is no outer object containing the "Info" property, which is a must. The valid JSON would be:
{
"Info": [
{
"Id":"",
"Name":"",
"Description":"",
"Date":"",
}
]
}
This is a JSON object that has a property "Info" which has a value that is a list of objects. This list of objects contains one object that has the properties "Id", "Name", "Description", and "Date", all of which have empty-string values.
Here is a quick tutorial on how to use GSON to parse a JSON feed such as the above JSON:
You will need a class to represent the items in the list:
public class InfoItem {
public String Id;
public String Name;
public String Description;
public String Date;
public InfoItem() {}
}
And one to represent the list of Items:
public class InfoItemList extends LinkedList<InfoItem> {
public InfoItemList() { super() };
}
This added complexity is because GSON cannot otherwise get the type of a generic collection from the class data.
And one to represent the overall JSON message:
public class InfoMessage {
public InfoItemList Info;
public InfoMessage() {};
}
And then just:
gson.fromJson(jsonString, InfoMessage.getClass());
If just de-serializing a collection:
Type listType = new TypeToken<List<InfoItem>>() {}.getType();
gson.fromJson(jsonString2, listType);
The Info object is a list because of the []. You have to use the following code to deserialze it:
EDIT:
public class Info {
// as in your question
public String name;
...
}
public class Data {
#SerializedName("Info")
public List<Info> info;
}
Then just use the data class to deserialize your json.

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