Nested JSONObject Deserialize to JSONObject - java

So I'm working on deserializing a nested JSONObject, but don't want to create a class for each nested object. I was trying to take on of the nested JSONObjects and put it in a JSONObject.
public class ContainerStatus {
#JsonProperty("name")
private String name;
#JsonProperty("state")
private JSONObject state;
#JsonProperty("lastState")
private JSONObject lastState;
#JsonProperty("ready")
private Boolean ready;
#JsonProperty("restartCount")
private Integer restartCount;
#JsonProperty("image")
private String image;
#JsonProperty("imageID")
private String imageID;
#JsonProperty("containerID")
private String containerID;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
Using this to deserialize:
{ "containerStatuses":
{
"name": "connect",
"state": {
"terminated": {
"exitCode": 1,
"reason": "Error",
"startedAt": "2019-03-20T15:40:08Z",
"finishedAt": "2019-03-20T15:40:50Z",
"containerID": "docker://"
}
},
"lastState": {},
"ready": true,
"restartCount": 0,
"image": "image",
"imageID": "docker-pullable://",
"containerID": "docker://"
}}
I get unrecognized field "terminated", because of the state JSONObject.
I want a:
JsonObject state = {
"terminated": {
"exitCode": 1,
"reason": "Error",
"startedAt": "2019-03-20T15:40:08Z",
"finishedAt": "2019-03-20T15:40:50Z",
"containerID": "docker://"
}
}
I can cast it into a generic object, but the format isn't JSON anymore:
#JsonProperty("state")
private Object state;
Gives me this format:
{running={startedAt=2019-03-20T14:53:53Z}}

You need to improve a little bit your example:
DeserializationFeature.UNWRAP_ROOT_VALUE, enable this feature to
unwrap JSON object.
Add JsonRootName annotation to your POJO class because class name does not match to property containerStatuses.
Use JsonNode which comes from Jackson library instead of JSONObject which comes probably from org.json.
Improved example could look like below:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonApp {
public static void main(String[] args) throws Exception {
String json = "{...}";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
ContainerStatus cs = mapper.readValue(json, ContainerStatus.class);
System.out.println(cs.getState());
}
}
#JsonRootName("containerStatuses")
class ContainerStatus {
#JsonProperty("name")
private String name;
#JsonProperty("state")
private JsonNode state;
#JsonProperty("lastState")
private JsonNode lastState;
#JsonProperty("ready")
private Boolean ready;
#JsonProperty("restartCount")
private Integer restartCount;
#JsonProperty("image")
private String image;
#JsonProperty("imageID")
private String imageID;
#JsonProperty("containerID")
private String containerID;
// getters, setters, toString
}
Above code prints:
{"terminated":{"exitCode":1,"reason":"Error","startedAt":"2019-03-20T15:40:08Z","finishedAt":"2019-03-20T15:40:50Z","containerID":"docker://"}}

Related

Deserialising complex nested Json using Jackson

I am struggling to deserialise complex nested Json data into Java objects I think my class structure is wrong. Here is my Json data:
{
"resultsPerPage": 20,
"startIndex": 0,
"totalResults": 2,
"result": {
"dataType": "CPE",
"feedVersion": "1.0",
"cpeCount": 2,
"feedTimestamp": "2021-03-19T13:06",
"cpes": [
{
"deprecated": false,
"cpe23Uri": "cpe:2.3:o:microsoft:windows_10:1511:*:*:*:*:*:x64:*",
"lastModifiedDate": "2015-12-09T17:28Z",
"titles": [
{
"title": "Microsoft Windows 10 1511 64-bit",
"lang": "en_US"
}
],
"refs": [
{
"ref": "https://www.microsoft.com/en-us/",
"type": "Vendor"
}
],
"deprecatedBy": [],
"vulnerabilities": [
"CVE-2016-0174",
"CVE-2016-0171"
]
}
Here is the class I map the Json data to:
public class RESPONSE {
Result result;
}
class Result {
List<Cpes> cpes;
}
class Cpes {
String cpe23Uri;
List<Titles> titles;
List<String> vulnerabilities;
}
class Titles{
String title;
}
When I debug my code r in the below code is null and I think it's because my RESPONSE class isn't set up right.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
RESPONSE r = mapper.readValue(response.getContent(), RESPONSE.class);
System.out.println(r);
Your object model should match the structure of the JSON you are trying to read. For example, it'll have to look something like the following:
public class Response {
private int resultsPerPage;
private int startIndex;
private int totalResults;
private Result result;
// Should include getters and setters
}
public class Result {
private String dataType;
private String feedVersion;
private int cpeCount;
private String feedTimestamp;
private CPE[] cpes;
// Should include getters and setters
}
public class CPE {
private boolean deprecated;
private String cpe23Uri;
private String lastModifiedDate;
private Title[] titles;
private Ref[] refs;
private String[] deprecatedBy;
private String[] vulnerabilities;
// Should include getters and setters
}
public class Title {
private String title;
private String lang;
// Should include getters and setters
}
public class Ref {
private String ref;
private String type;
// Should include getters and setters
}
Note that to keep the code sample short, I've omitted the getters and setters.
Edit: As Tugrul pointed out below, since fail on unknown property is disabled, it won't fail if there are missing fields in your model. The only issue is the missing getters and setters.
I also found another way to solve this issue for future reference.
I used a tree data structure to access my Json fields which means I can just declare a flat class:
public class Test {
private String cpe23Uri;
private String title;
private List<String> vulnerabilities;
public String getCpe23Uri() {
return cpe23Uri;
}
public void setCpe23Uri(String cpe23Uri) {
this.cpe23Uri = cpe23Uri;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getVulnerabilities() {
return vulnerabilities;
}
public void setVulnerabilities(List<String> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
}
}
I then mapped using a Tree
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode resultNode = mapper.readTree(response.getContent());
Test t = new Test();
t.setCpe23Uri(resultNode.get("result").get("cpes").get(0).get("cpe23Uri").textValue());

How to create Jackson XML POJO class for a JsonObject of JsonObjects

I'm trying to create POJOs for the following JSON structure. The Fields node is easy enough to wire up, but I'm unsure how to use annotations to wire up the Description node. If I had been defining the JSON structure for that node, I'd have create an JsonArray of JsonObjects, which would make the java class easy, but since I didn't, I need to figure out how to serialize the structure below:
{
"Fields": {
"Required": ["ftp.hostname"],
"Optional": ["ftp.rootDirectory"]
},
"Description": {
"ftp.hostname": {
"label": "SFTP Hostname",
"description": "SFTP server hostname or IP address"
},
"ftp.rootDirectory": {
"label": "Root Directory",
"description": "The root path on the Data Store accessible by this connector"
}
}
}
Note that the nodes in the Description object have names that correlate to the values defined in the Fields node, which means their node names can vary from payload to payload.
The class for the Fields node:
public class FieldDetails {
public static final String REQUIRED = "Required";
public static final String OPTIONAL = "Optional";
#JsonProperty(value = REQUIRED, required = true)
private List<String> required;
#JsonProperty(value = OPTIONAL, required = true)
private List<String> optional;
}
And what I have so far for the entire object:
public class FieldDefinitions {
public static final String FIELDS = "Fields";
public static final String DESCRIPTION = "Description";
#JsonProperty(value = FIELDS, required = true)
private FieldDetails fields;
#JsonProperty(value = DESCRIPTION , required = true)
private ??? descriptions;
}
Generally, you can always map any JSON object to Map<String, Object>. If JSON is complicated with many nested objects, Jackson will automatically pick correct type: Map for objects and List for arrays.
You can also declare class like below for Description properties.
class Description {
private String label;
private String description;
// getters, setters, toString
}
The whole Description is a big JSON which you can map to Map<String, Description>. So, it could look like below:
class FieldDefinitions {
public static final String FIELDS = "Fields";
public static final String DESCRIPTION = "Description";
#JsonProperty(value = FIELDS, required = true)
private FieldDetails fields;
#JsonProperty(value = DESCRIPTION, required = true)
private Map<String, Description> descriptions;
// getters, setters, toString
}
Rest is the same. Example app:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.List;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
File json = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
FieldDefinitions fields = mapper.readValue(json, FieldDefinitions.class);
System.out.println("Required");
fields.getFields().getRequired().forEach(r ->
System.out.println(r + " = " + fields.getDescriptions().get(r)));
System.out.println("Optional");
fields.getFields().getOptional().forEach(r ->
System.out.println(r + " = " + fields.getDescriptions().get(r)));
}
}
For given JSON payload prints:
Required
ftp.hostname = Description{label='SFTP Hostname', description='SFTP server hostname or IP address'}
Optional
ftp.rootDirectory = Description{label='Root Directory', description='The root path on the Data Store accessible by this connector'}
That's the structure.
public class FieldDefinitions {
#JsonProperty("Fields")
public FieldDetails fields = new FieldDetails();
#JsonProperty("Description")
public Map<String, Property> properties = new HashMap<>();
}
public class FieldDetails {
#JsonProperty("Required")
public List<String> required = new ArrayList<>();
#JsonProperty("Optional")
public List<String> optional = new ArrayList<>();
}
public class Property {
public String label;
public String description;
}

Mapping specific JSON to Java object

I'm trying to map specific JSON to my Java Model class. And am having trouble mapping it to the Java object.
I'm using fasterxml (jackson) to map JSON to my Java Model class below - CurrencyModel.java. This JSON have '[' at the beginning which it probably means that it is an array. I cannot map it into my class, CurrencyModel.java
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
class CurrencyModel {
protected List<Currencies> currencies;
protected List<CurrenciesRates> currenciesRates;
#Data
#NoArgsConstructor
#AllArgsConstructor
static class Currencies {
#JsonProperty("table")
private String table;
#JsonProperty("no")
private String no;
#JsonProperty("effectiveDate")
private String effectiveDate;
#JsonProperty("rates")
private ArrayList<CurrencyRatesModel> rates;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
static class CurrenciesRates {
#JsonProperty("currency")
private String currency;
#JsonProperty("code")
private String code;
#JsonProperty("mid")
private String mid;
}
}
And having JSON below in String variable
[
{
"table": "A",
"no": "064/A/NBP/2013",
"effectiveDate": "2013-04-02",
"rates": [
{
"currency": "bat (Tajlandia)",
"code": "THB",
"mid": 0.1108
},
{
"currency": "dolar amerykański",
"code": "USD",
"mid": 3.2552
},
{
"currency": "dolar australijski",
"code": "AUD",
"mid": 3.4048
}
]
}
]
I'm trying to run this code using:
ObjectMapper objectMapper = new ObjectMapper();
final String output = "[{\"table\": \"A\", \"no\": \"064/A/NBP/2013\",\"effectiveDate\": \"2013-04-02\",\"rates\": [{\"currency\": \"bat (Tajlandia)\",\"code\": \"THB\",\"mid\": 0.1108},{\"currency\": \"dolar amerykański\",\"code\": \"USD\",\"mid\": 3.2552},{\"currency\": \"dolar australijski\",\"code\": \"AUD\",\"mid\": 3.4048}]}]";
List<CurrencyModel> currencyModelList = Arrays.asList(objectMapper.readValue(output, CurrencyModel.class));
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
System.out.println(currencyModelList.toArray());
which results in error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `` out of START_ARRAY token
I see problem with your java class. I have executed your input json and come with mentioned below java class mapping.
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CurrencyModel {
#JsonProperty("table")
private String table;
#JsonProperty("no")
private String no;
#JsonProperty("effectiveDate")
private String effectiveDate;
#JsonProperty("rates")
private ArrayList<CurrenciesRates> rates;
#Data
#NoArgsConstructor
#AllArgsConstructor
static class CurrenciesRates {
#JsonProperty("currency")
private String currency;
#JsonProperty("code")
private String code;
#JsonProperty("mid")
private String mid;
}
}
And your main class,
ObjectMapper objectMapper = new ObjectMapper();
final String output = "[{\"table\": \"A\", \"no\": \"064/A/NBP/2013\",\"effectiveDate\": \"2013-04-02\",\"rates\": [{\"currency\": \"bat (Tajlandia)\",\"code\": \"THB\",\"mid\": 0.1108},{\"currency\": \"dolar amerykański\",\"code\": \"USD\",\"mid\": 3.2552},{\"currency\": \"dolar australijski\",\"code\": \"AUD\",\"mid\": 3.4048}]}]";
List<CurrencyModel> myObjects = objectMapper.readValue(output, new TypeReference<List<CurrencyModel>>() {
});
Your json contains list of object. So use TypeReference
ObjectMapper objectMapper = new ObjectMapper();
final String output = "[{\"table\": \"A\", \"no\": \"064/A/NBP/2013\",\"effectiveDate\": \"2013-04-02\",\"rates\": [{\"currency\": \"bat (Tajlandia)\",\"code\": \"THB\",\"mid\": 0.1108},{\"currency\": \"dolar amerykański\",\"code\": \"USD\",\"mid\": 3.2552},{\"currency\": \"dolar australijski\",\"code\": \"AUD\",\"mid\": 3.4048}]}]";
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
List<CurrencyModel> currencyModelList = objectMapper.readValue(output, new TypeReference<List<CurrencyModel.Currencies>>(){});
System.out.println(currencyModelList);

Map JSON to pojo using Jackson for List that have different parameters

JSON FORMAT:
[
{
"0":
{
"cast":"",
"showname":"woh pagle",
"type":"Episodes"
},
"video":[
{
"src":"video.mp4"
},
{
"DRM":"False"
}
]
}
]
Here problem is I am getting below exception:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize
instance of java.util.ArrayList out of START_OBJECT token at [Source:
java.io.StringReader#1c9ca1; line: 1, column: 55617] (through
reference chain:
com.apalya.myplex.valueobject.ThirdPartyContentDetailsArray["video"])
My pojo classes are :
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonProperty("0")
private ThirdPartySubContentDetails subContent;
#JsonProperty("video")
private List<ThirdPartySubContentVideoInfo> video;
My Sub class pojo is :
private String src;
#JsonIgnore
#JsonProperty("DRM")
private String drm;
Please help me to write a pojo for that video list.
Your json starts as an array and not as an Object. The important part to change is how the Objectmapper should generate your json. For returning a List you need to do it this way:
List<FirstJson> jsonList = mapper.readValue(json, new TypeReference<List<FirstJson>>(){});
Here is my short working test I implement locally:
public static void main(String[] args) {
String json = "[{\"0\":{\"cast\":\"\",\"showname\":\"wohpagle\",\"type\":\"Episodes\"},\"video\":[{\"src\":\"video.mp4\"},{\"DRM\":\"False\"}]}]";
ObjectMapper mapper = new ObjectMapper();
List<FirstJson> jsonList = mapper.readValue(json, new TypeReference<List<FirstJson>>(){});
System.out.println(jsonList.toString());
}
The first part of your JsonArray in Pojo.(Named it FirstJson)
public class FirstJson{
#JsonProperty("0")
private FirstJson subContent;
private String cast;
private String showname;
private String type;
#JsonProperty("video")
private List<Video> videos;
//getter/setter
And the Video Pojo:
public class Video {
private String src;
#JsonProperty("DRM")
private String drm;
//getter/setter
Just a sidenote: If you declare your pojos in the same class file, the classes should be static. public static class FirstJson
According to the JSON structure described in the question, the following should be the POJOs:
public class MainPojo
{
#JsonProperty("0")
private ThirdPartySubContentDetails subContent;
#JsonProperty("video")
private List<ThirdPartySubContentVideoInfo> video;
// Getters and Setters for subContent and video
}
class ThirdPartySubContentDetails
{
private String cast;
private String showName;
private String type;
// Getters and Setters for cast, showName and type
}
#JsonIgnoreProperties(ignoreUnknown = true)
class ThirdPartySubContentVideoInfo
{
#JsonProperty("src")
private String src;
#JsonProperty("DRM")
private String drm;
// Getters and Setters for src and drm
}
You should call the deserializer method as follows:
List<MainPojo> list = new ObjectMapper().readValue(json, new TypeReference<List<MainPojo>>(){});

Jackson parse JSON to Map<String, TypeA>

I have the following JSON:
{
"data": {
"1": {
"id":"1",
"name":"test1"
},
"2": {
"id":"2",
"name":"test2"
}
}
}
I want to parse the "data" into an Object with Jackson. If I parse it as Map<String, Object> it works well, whereas "1", "2" (...) are used as Key with the respective data as value, represented by a Map again.
Now I want to parse this JSON to Map<String, TypeA> whereas class TypeA would have two fields, id and name.
Can someone give me a hint how to to that?
I always get the following error:
Could not read JSON: No suitable constructor found for type [simple
type, class TypeA]: can not instantiate from JSON object (need to
add/enable type information?)
Thanks a lot in advance,
tohoe
The following should work out for you.
public class MyDataObject {
private final Map<String, TypeA> data;
#JsonCreator
public MyDataObject(#JsonProperty("data") final Map<String, TypeA> data) {
this.data = data;
}
public Map<String, TypeA> getData() {
return data;
}
}
public class TypeA {
private final String id;
private final String name;
#JsonCreator
public TypeA(#JsonProperty("id") final String id,
#JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
The #JsonCreator is used for describing how to create your objects together with the name of the properties #JsonProperty. Even if they are nested.
To deserialize the whole thing:
ObjectMapper mapper = new ObjectMapper();
final MyDataObject myDataObject = mapper.readValue(json, MyDataObject.class);

Categories

Resources