Deserialize json to java object using jackson. Design Patterns - java

json:
{
"events": [
{
"child_id": "unknown",
"some_info": "text",
"data": {
<some string>: {
"id": 1,
"prop": "propValue"
}
}
},
{
"child_id": "known",
"some_info": "text1",
"data": {
"id": 2,
"prop": "propValue1"
}
]
}
i am using structure like this:
public inteface EventService {
void handle();
}
public class EventsDto {
private List<Event> events;
//getter, setter, constructors
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "child_id")
#JsonSubTypes({
#JsonSubTypes.Type(value = UnknownEvent.class, name = "unknown"),
#JsonSubTypes.Type(value = KnownEvent.class, name = "known")
})
public static abstract class Event implements EventService {
#JsonProperty("some_info")
private String someInfo;
//getter, setter, constructors
}
}
public class UnknownEvent extends EventsDto.Event {
private JsonNode data;
//getter, setter, constructors
#Override
public void handle() {
//something is happening
}
}
public class KnownEvent extends EventsDto.Event {
private Data data;
//getter, setter, constructors
#Override
public void handle() {
//something is happening
}
public static class Data {
private Integer id;
private String prop;
//getters, setters, constructors
}
}
public class Main {
public static void main(String[] args) {
String json = "<json>";
EventsDto eventsDto = objectMapper.readValue(json, EventsDto.class);
eventsDto.getEvents().forEach(event -> event.handle());
}
}
Is it possible to do something with UnknownEvent, to deserialize into a java object with all fields like KnownEvent without using JsonNode?
some string in json is dynamic and cannot be known in advance.
How can I pull the implementation of the "handle" method from the DTO into a separate class? If possible - do not change the logic of the main class.
I would like to hear advice on how to improve the code.

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

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

How to generate pojo with Lombok for complex json in Eclipse

Below is the json for pojo creation. I want to create a pojo using Lombok.
I am new to rest assured. How can I create a pojo using Lombok in Eclipse. I want in for nested json, like below Jira API post body request.
{
"fields": {
"project": {
"key": "RA"
},
"summary": "Main order flow broken",
"description": "Creating my fist bug",
"issuetype": {
"name": "Bug"
}
}
}
I have created the below pojo manually, and I am not sure if it's correct. How can I call the generated pojo in post body?
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class createissue {
private fieldss fields;
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public static class fieldss {
private Project poject;
private Sting summary;
private String description;
private Issuetype issuetypessuetype;
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Project {
private Sting key;
}
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Issuetype {
private Sting name;
}
}
The POJO is correct, It had some typos which I have corrected
public class Lombok {
public static #Data class fieldss {
private Project project;
private String summary;
private String description;
private Issuetype issuetype;
}
public static #Data class createissue {
private fieldss fields;
}
public static #Data class Issuetype {
private String name;
}
public static #Data class Project {
private String key;
}
}
and the below is how you can test
public static void main(String[] args) {
// TODO Auto-generated method stub
Issuetype a1 = new Issuetype();
a1.setName("Bug");
Project a2 = new Project();
a2.setKey("RA");
fieldss a3 = new fieldss();
a3.setDescription("Creating my fist bug");
a3.setSummary("Main order flow broken");
a3.setIssuetype(a1);
a3.setProject(a2);
createissue a4 = new createissue();
a4.setFields(a3);
ObjectMapper mapper = new ObjectMapper();
String abc = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(a4);
System.out.println(abc);
}
You should be able to see the below in the console
{
"fields": {
"project": {
"key": "RA"
},
"summary": "Main order flow broken",
"description": "Creating my fist bug",
"issuetype": {
"name": "Bug"
}
}
}

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 do I parse a JSON array into different objects using Jackson on Android?

I'm trying to parse JSON like the following into object using Jackson on Android (Note: I'm not in control of the JSON format - the format comes from Yammer)
"references": [
{
"type": "user",
"id": 12345678,
"name": "Wex"
},
{
"type": "message",
"id": 12345679,
"body":
{
"plain":"A short message"
}
},
{
"type": "thread",
"id": 12345670,
"thread_starter_id": 428181699
}
]
The problem is that each entry in references is a different type of object with different properties. As a start I've got:
public static class Reference
{
public String type;
public String id;
}
I'd rather avoid putting all potential properties in one object like:
public static class Reference
{
public static class Body
{
public String plain;
}
public String type;
public String id;
public String name;
public Body body;
public String thread_starter_id;
}
And want to use separate classes that are created dependant on the type value like:
public static class ReferenceUser extends Reference
{
public String name;
}
public static class ReferenceMessage extends Reference
{
public static class Body
{
public String plain;
}
public Body body;
}
public static class ReferenceThread extends Reference
{
public String thread_starter_id;
}
So... what's the best way of getting Jackson to parse the JSON like that?
I'm currently parsing it quite simply like this:
ObjectMapper mapper = new ObjectMapper();
Reference[] references = mapper.readValue(json, Reference[].class);
you can do something like this with Jackson:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(name = "user", value = ReferenceUser.class),
#JsonSubTypes.Type(name = "message", value = ReferenceMessage.class),
#JsonSubTypes.Type(name = "thread", value = ReferenceThread.class)
})
public class Reference {
int id;
String name;
}
This way you will generate subclasses.
John

Categories

Resources