GSON deserialization of object arrays - java

I have a class with the following attributes
public class JenkinsServer
{
private String url;
private String mode;
private String nodeName;
private String nodeDescription;
private String description;
private boolean useSecurity;
private boolean quietingDown;
private JenkinsServerView primaryView;
private List< JenkinsJob > jobs;
private List< JenkinsServerView > views;
}
Now I want GSON to deserialize/map a json document to it. It works well, except for my lists - they are empty. The json document looks as follows (snippet):
"jobs": [
{
"name": "AnotherJob",
"url": "https://build.example.com/jenkins/job/AnotherJob/",
"color": "disabled"
},
{
"name": "AnotherJob2",
"url": "https://build.example.com/jenkins/job/Build%20CI%20Build/",
"color": "blue"
},
"views": [
{
"name": "-All Views",
"url": "https://build.example.com/jenkins/view/-All%Views/"
},
{
"name": "Alle",
"url": "https://build.example.com/jenkins/"
},
The mapping works, even for the single instance of
JenkinsServerView primaryView
but not for the Lists. I'm starting the mapping this way:
Gson gson = gsonBuilder.create();
JenkinsServer server = gson.fromJson( reader, JenkinsServer.class );

looks your json data that you are trying to parse is invalid.
In your json jobs and views are arrays and both of them doesn't have the closing brace at the end.
The valid json will be as follows: (Observe the closing braces at the end of the array)
{
"jobs": [
{
"name": "AnotherJob",
"url": "https://build.example.com/jenkins/job/AnotherJob/",
"color": "disabled"
},
{
"name": "AnotherJob2",
"url": "https://build.example.com/jenkins/job/Build%20CI%20Build/",
"color": "blue"
}
],
"views": [
{
"name": "-All Views",
"url": "https://build.example.com/jenkins/view/-All%Views/"
},
{
"name": "Alle",
"url": "https://build.example.com/jenkins/"
}
]
}

Related

Deserializing complex json with matching objects by id (Jackson)

I have a proprietary API that return a complex JSON like:
{
"store": "store_name",
"address": "store_address",
"department": [
{
"name": "d1",
"type": "t1",
"items": [
"i1",
"i2"
]
},
{
"name": "d2",
"type": "t2",
"items": [
"i3"
]
}
],
"itemDescriptions": [
{
"id": "i1",
"description": "desc1"
},
{
"id": "i2",
"description": "desc2",
"innerItems": [
"i2"
]
},
{
"id": "i3",
"description": "desc3"
}
]
}
Is it possible to deserialize this JSON using Jackson into:
#AllArgsConstructor
class Store {
private final String store;
private final String address;
private final List<Department> departments;
/*some logic*/
}
#AllArgsConstructor
class Department {
private final String name;
private final String type;
private final List<Item> items;
/*some logic*/
}
#AllArgsConstructor
class Item {
private final String id;
private final String description;
private final List<Item> innerItems;
/*some logic*/
}
I tried to find answers, but find only this question without solution.
I know that I can do it in my code (deserialize as it is and create objects from result), but its very memory intensive (I have a lot of json and it can be large).
I know that I can write fully custom deserializer, but in this case, I have to describe the deserialization of each field myself - in case of some changes, I will have to change the deserializer, and not just the class(POJO/DTO).
Is there a way to do this with Jackson (or Gson) or with a minimal (preferably relatively generic) amount of my code?

how to serialize a specific json format

I need to set json in a format provided below.
"Tyre": {
"title": "Tyre",
"cls": "TyreCondition",
"items": [
{
"firstItem": [
{
"title": "Front right tyre",
"value": "50%",
"subValue": "30,000 km Approx"
}
],
"secItem": [
{
"title": "title",
"value": "Front right tyre tread - 50%"
},
{
"title": "title",
"value": "Front right tyre tread - 50%"
}
]
}
]
}
class that i have created for this is looks like:
private String title;
private String cls;
#SerializedName("firstItem")
private List<UsedCarInspectionInfo> items;
#SerializedName("secItem")
private List<UsedCarTyreImage> imageList;
//getter and setter
when i run this code with this class structure, I am getting the json like-
"Tyre": {
"title": "Tyre",
"cls": "TyreCondition",
"firstItem": [
{
"title": "Front right tyre",
"value": "50%",
"subValue": "30,000 km Approx"
}
],
"secItem": [
{
"title": "title",
"value": "Front right tyre tread - 50%"
},
{
"title": "title",
"value": "Front right tyre tread - 50%"
}
]}
Any idea how i can get the firstItem and secItem array in Items array?
You get no items key in your JSON, because you override the items field in your Java class with #SerializedName.
You need something like this:
String title;
String cls;
List<Item> items;
static class Item {
List<FirstItem> firstItem;
List<SecItem> secItem;
}
static class FirstItem {
String title;
String value;
String subValue;
}
static class SecItem {
String title;
String value;
}

How to interpret JSON format in android? [duplicate]

This question already has answers here:
How to parse JSON in Java
(36 answers)
How do I parse JSON in Android? [duplicate]
(3 answers)
Closed 6 years ago.
I have a url which returns the following, how can I save the data in 3 arrays (Item1Prices[], Item2Prices[], Categories[]) on android? The item prices correspond with the months in the "categories" array. I'm following this tutorial but I need help in modifying the logic in onResponse() to work with mine.
I've seen some examples of this done but I am having trouble recognizing what is what for my case specifically. If someone were to help me get started I would be fine afterwards.
{
"dataset": [
{
"seriesname": "Item1 Price",
"data": [
{
"value": 4.72
},
{
"value": 2.81
},
{
"value": 6.18
},
{
"value": 5.17
},
{
"value": 2.94
},
{
"value": 3.77
},
{
"value": 1.7
},
{
"value": 6.72
},
{
"value": 4.61
}
]
},
{
"seriesname": "Item2 Price",
"data": [
{
"value": 2.49
},
{
"value": 0.72
},
{
"value": 4.06
},
{
"value": 1.74
},
{
"value": 7.23
},
{
"value": 5.83
},
{
"value": 2.59
},
{
"value": 7.54
},
{
"value": 7.02
}
]
}
],
"categories": [
{
"label": "Jan"
},
{
"label": "Feb"
},
{
"label": "Mar"
},
{
"label": "Apr"
},
{
"label": "May"
},
{
"label": "Jun"
},
{
"label": "Jul"
},
{
"label": "Aug"
},
{
"label": "Sept"
}
]
}
I would go with Gson for the parsing.
That way you build annotated POJOs like:
public class MyData{
#Expose #SerializedName("dataset") ArrayList<Series> dataSet;
#Expose #SerializedName("categories") ArrayList<Category> categoryList;
}
and
public class Series {
#Expose #SerializedName("seriesname") String seriesName;
#Expose #SerializedName("data") ArrayList<DataValue> data;
}
and
public class DataValue{
#Expose #SerializedName("data") float dataValue;
}
and
public class Category{
#Expose #SerializedName("label") String label;
}
Then when you need to parse you use:
Gson gson = new GsonBuilder().create();
MyData data = gson.fromJson(jsonString, MyData.class);

Deserialize complex JSON to Java, classes nested multiple levels deep

I am trying to make the Json output from Cucumber into a single Java object. This contains objects nested four levels deep, and I am having trouble deserializing it. I am presently using Jackson, but open to suggestions.
Here is my Json code:
{
"line": 1,
"elements": [
{
"line": 3,
"name": "Converteren centimeters naar voeten/inches",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa;converteren-centimeters-naar-voeten/inches",
"type": "scenario",
"keyword": "Scenario",
"steps": [
{
"result": {
"duration": 476796588,
"status": "passed"
},
"line": 4,
"name": "maak Maten-object aan met invoer in \"centimeters\"",
"match": {
"arguments": [
{
"val": "centimeters",
"offset": 37
}
],
"location": "StepDefinition.maakMatenObjectAanMetInvoerIn(String)"
},
"keyword": "Given "
},
{
"result": {
"duration": 36319,
"status": "passed"
},
"line": 5,
"name": "ik converteer",
"match": {
"location": "StepDefinition.converteerMaten()"
},
"keyword": "When "
},
{
"result": {
"duration": 49138,
"status": "passed"
},
"line": 6,
"name": "uitvoer bevat maat in \"voeten/inches\"",
"match": {
"arguments": [
{
"val": "voeten/inches",
"offset": 23
}
],
"location": "StepDefinition.uitvoerBevatMaatIn(String)"
},
"keyword": "Then "
}
]
},
{
"line": 8,
"name": "Converteren voeten/inches naar centimeters",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa;converteren-voeten/inches-naar-centimeters",
"type": "scenario",
"keyword": "Scenario",
"steps": [
{
"result": {
"duration": 84175,
"status": "passed"
},
"line": 9,
"name": "maak Maten-object aan met invoer in \"voeten/inches\"",
"match": {
"arguments": [
{
"val": "voeten/inches",
"offset": 37
}
],
"location": "StepDefinition.maakMatenObjectAanMetInvoerIn(String)"
},
"keyword": "Given "
},
{
"result": {
"duration": 23928,
"status": "passed"
},
"line": 10,
"name": "ik converteer",
"match": {
"location": "StepDefinition.converteerMaten()"
},
"keyword": "When "
},
{
"result": {
"duration": 55547,
"status": "passed"
},
"line": 11,
"name": "uitvoer bevat maat in \"centimeters\"",
"match": {
"arguments": [
{
"val": "centimeters",
"offset": 23
}
],
"location": "StepDefinition.uitvoerBevatMaatIn(String)"
},
"keyword": "Then "
}
]
}
],
"name": "Applicatie neemt maten in cm en converteert ze naar voet/inch, en vice versa",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa",
"keyword": "Feature",
"uri": "sample.feature"
}
I have tried a number of different approaches. First I used nested inner classes, but it appeared you had to make them static, which I feared would not work since I have multiple instances of the same object within one (multiple "element"-objects in the root, for example). Then I tried putting them in separate classes, with Json annotations. Here's where that got me (omitting setters):
public class CucumberUitvoer {
private String name;
private String description;
private String id;
private String keyword;
private String uri;
private int line;
#JsonProperty("elements")
private List<FeatureObject> elements;
public CucumberUitvoer(){}
}
public class FeatureObject {
private String name;
private String description;
private String id;
private String type;
private String keyword;
private int line;
#JsonProperty("steps")
private List<StepObject> steps;
public FeatureObject() {
}
}
public class StepObject {
#JsonProperty("result")
private ResultObject result;
private String name;
private String given;
private String location;
private String keyword;
private int line;
#JsonProperty("match")
private MatchObject match;
public StepObject(){}
}
public class ResultObject {
private int duration;
private String status;
public ResultObject(){}
}
public class MatchObject {
#JsonProperty("arguments")
private List<ArgumentObject> arguments;
private String location;
public MatchObject(){}
}
public class ArgumentObject {
private String val;
private String offset;
public ArgumentObject(){}
}
For clarification, here's a class diagram of how the nesting works.
This solution gives me the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of nl.icaprojecten.TestIntegratieQuintor.JSONInterpreter.CucumberUitvoer out of START_ARRAY token
Here is the code doing the actual mapping:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
CucumberUitvoer obj1 = null;
try {
obj1 = mapper.readValue(json, CucumberUitvoer.class);
} catch (IOException e) {
e.printStackTrace();
}
Is there a quick fix to this approach to make it work, or should I try something entirely different?
Ok I spent some time debugging and trying to figure out what was the problem, and finally was something pretty obvious.
implements Serializable
Thats the line I added to MatchObject and worked.
When we try to deserialize some object first we have to make those classes implements the interface Serializable
I just tried your sample code and oddly, it works.
Can you please double check your imports, if the JSON is coming in as provided and the getters, setters, constructors are actually there?
You can get the idea from this code to deserialize,
public class testCustomDeSerializer extends JsonDeserializer<test> {
public testCustomDeSerializer() {
this(null);
}
public TestCustomDeSerializer(Class t) {
// super(t);
}
#Override
public Test deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
ObjectCodec objectCodec = p.getCodec();
JsonNode node = objectCodec.readTree(p);
ObjectMapper objectMapper = new ObjectMapper();
Test test= new Test();
test.setId(node.get("line").asText());
List<elements> elementList = new ArrayList<>();
JsonNode elementsNode = node.get("elements");
Iterator<JsonNode> slaidsIterator = elementsNode.elements();
while (slaidsIterator.hasNext()) {
Steps steps= new Steps();
JsonNode slaidNode = slaidsIterator.next();
JsonNode stepNode= (JsonNode) slaidNode.get("Steps");
BoundingPoly in = objectMapper.readValue(stepNode.toString(), Steps.class);
elementsNode.setSteps(in);
/// continue
return
}
Hope it helps

How to ignore wrap element when parse json to dto

I have a json like this
{
"135": {
"id": "135",
"name": "My Awesome Washing Machine!",
"powerswitch": {
"available": "true",
"state": "on",
"reachable": "true",
"locked": "false"
},
"reference": {
"id": "4",
"name": "Lave-linge",
"category_id":"2"
}
},
"491": {
"id": "491",
"name": "My Fridge",
"powerswitch": {
"available": "true",
"state": "on",
"reachable": "false",
"locked": "false"
},
"reference": {
"id": "1",
"name": "Réfrigérateur",
"category_id":"1"
}
}
}
And here is my dto:
public class Device {
private String id;
private String name;
private DevicePowerswitch powerswitch;
private DeviceReference reference;
//getter, setter
}
The question is how can I parse json to a list of device.
Note that there is a non-static id value wrapper in this above json.
You'll need to parse the JSON into a JsonNode, and then iterate over the children. The nodes you pull out as a result can then be mapped using an ObjectMapper to Device instances.
ObjectMapper mapper = new ObjectMapper();
final JsonNode jsonNode = mapper.readTree(JSON);
for (JsonNode node : jsonNode)
{
final Device device = mapper.convertValue(node,
Device.class);
// do something with the device
}

Categories