Deserialising complex nested Json using Jackson - java

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());

Related

Jackson-databind mapping JSON skip layer

I got a JSON response like this:
{
"status": "success",
"response": {
"entries": [
{
"id": 1,
"value": "test"
},
{
"id": 2,
"value": "test2"
}
]
}
}
And i want to map it with jackson-databind on an object like this:
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response.entries")
private Collection<ResponseEntry> entries;
}
So i'm searching for an way to give #JsonProperty a path so it can skip the layer "response".
Welcome to Stack Overflow. You can define a wrapper class for your Collection<ResponseEntry> collection like below :
public class ResponseWrapper {
#JsonProperty("entries")
private Collection<ResponseEntry> entries;
}
The ResponseEntry class could be defined like below :
public class ResponseEntry {
#JsonProperty("id")
private int id;
#JsonProperty("value")
private String value;
}
Once defined these classes you can rewrite your old Response class like below :
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response")
private ResponseWrapper responseWrapper;
}
You can flatten using the #JsonUnwrapped annotation.
You can have your classes like this
public class Response {
private String status;
private Collection<ResponseEntry> entries;
}
public class ResponseEntry {
#JsonUnwrapped
private Entry entry;
}
pubic class Entry{
private Integer id;
private String value;
}

Is there a way to unmarshall an array inside an object directly into a list using Jackson

I have a very simple JSON:
{
"fruits": [
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
}
I can unmarshall this JSON easily using the default ObjectMapper that comes with Java and two simple POJO classes which look like this:
public class FruitList {
private List<Fruit> fruits;
public List<Fruit> getFruits() {
return fruits;
}
public void setFruits(List<Fruit> fruits) {
this.fruits = fruits;
}
}
And the individual Fruit elements:
public class Fruit {
private String name;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
This works and is perfectly acceptable. However, I would like to know it it is possible to unmarshal this JSON directly into a List<Fruit> - somehow ignoring the container object called fruits?
I tried using this feature but it did not work:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
Also experimenting with:
List<Fruit> list = mapper.readValue(jsonString,
TypeFactory.defaultInstance().constructCollectionType(List.class, Fruit.class));
Did not do the trick.
Thanks for any help,
Cheers

GSON Json Mapping for HashMap Nested Object

I have a json context like below:
{
"data": {
"details": {
"en-CA": {
"languageCode": "en-CA",
"isPrimaryLocale": false
},
"en-US": {
"languageCode": "en-US",
"isPrimaryLocale": true,
"languageDisplayName": "English (United States)",
}
}
}
}
To map it with GSON in java:
I created this classes:
public class ApiResponseSingleDto
{
private ResponseDetail data;
}
public class ResponseDetail
{
private ResponseDetails details;
#Getter
public static class ResponseDetails
{
public HashMap<String, LocaleDetail> row = new HashMap<>();
}
}
public class LocaleDetail
{
private String languageCode;
private Boolean isPrimaryLocale;
private String languageDisplayName;
}
When I try to map json to Java POJO class, HashMap doesn't work. Is there any suggestion?
To map it:
GSON.fromJson("...json", Type type...);
Just try to replace:
public class ApiResponseSingleDto
{
private ResponseDetail data;
}
public class ResponseDetail
{
private Map<String, LocaleDetail> details;
}
public class LocaleDetail
{
private String languageCode;
private Boolean isPrimaryLocale;
private String languageDisplayName;
}
Also json seems to be incorrect: "languageDisplayName": "English (United States)",
should be just "languageDisplayName": "English (United States)"
One more note: I believe you should have public fields or at least getters for them

How to deserialize JSON response using #JsonProperty?

I have this object which is converted into following format but it does not wrap it properly.
#JsonProperty("code")
private String code;
#JsonProperty("message")
private String msg;
#JsonProperty("assign")
private SomeVO someVO;
//getter, setters
to this format:
{
"status": {
"code": $value,
"message": $value
},
"data":{
"assign" {
"schemaLayoutFileName" : $value
"dataStoreTargetLocationText" : $value
}
}
}
How can it be done?
The class you have defined does not match the JSON you want to parse. Try the following design (if the class attributes names match the JSON properties names, you won't need #JsonProperty):
public class Foo {
private Status status;
private Data data;
// Getters and setters
}
public class Status {
private String code;
private String value;
// Getters and setters
}
public class Data {
private Assign assign;
// Getters and setters
}
public class Assign {
private String schemaLayoutFileName;
private String dataStoreTargetLocationText;
// Getters and setters
}

Using Jackon ObjectMapper to map to array of classes, alongside other fields?

I have some Json in the following form:
"items": [
{
"id": 1,
"text": "As a user without a subscription, I get a choice of available ones.",
"status": "finished",
"tags": [
{
"id": 1234,
"name": "feature=subs"
},
{
"id": 1235,
"name": "epic=premium"
}
]
},
{
"id": 2,
...
There are more fields but I have ommitted them for clarity.
I am trying to map each story to a Story Class with fields ID, Text, Status and a list of Tags. I've got it working fine using the following:
public Project JsonToProject(byte[] json) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode rootNode = mapper.readValue(json, JsonNode.class);
int storyCount = rootNode.get("totalItems").asInt();
ArrayNode itemsNode = (ArrayNode) rootNode.get("items");
Project project = new Project();
for (int i = 0; i < storyCount; i++)
{
Story story = JsonToStory(rootNode.get(i));
project.addStory(story);
}
return project;
}
Where a project is simple an ArrayList of Stories, and JsonToStory is the following method:
public Story JsonToStory(JsonNode rootNode) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Story story = mapper.readValue(rootNode, Story.class);
return story;
}
The Story Class is as follows:
public class Story {
private int id;
private String text = new String();
private String status = new String();
private final List<Tag> tags = new ArrayList<Tag>();
public void setId(int i)
{
id = i;
}
public void setText(String s)
{
text = s;
}
public void setStatus(String s)
{
status = s;
}
public void setTags(Tag[])
{
???
}
}
with the get methods and print methods. The Tag Class simply contains two string fields.
I don't know how to structure the setTags method, in order to result in an arraylist of Tag objects, and haven't been able to find anything to help.
Thanks!
You have marked your tags as final, which will probably block the setter from setting the tags. You can try this:
public class Story {
private int id;
private String text = new String();
private String status = new String();
private List<Tag> tags;
public void setTags(List<Tag> tags){
this.tags = tags;
}
OR
public class Story {
private int id;
private String text = new String();
private String status = new String();
private Tag[] tags;
public void setTags(Tag[] tags){
this.tags = tags;
}

Categories

Resources