Parsing nested JSON containing both Objects and single values - java

I have what I believe is called nested JSON and I want to use Jackson to deserialize into objects. Is it possible to automatically parse the child objects into Java Objects as well if a Program class had for example objects of the type TrackedEntity and ProgramStage (see JSON code) ? Alternatively would it be possible to simply parse the "id" of the respective objects and put them in Strings in the Program objects?
JSON Example is as follows:
{
programs:
[
{
"id": "IpHINAT79UW",
"created": "2013-03-04T10:41:07.494+0000",
"trackedEntity":
{
"id": "cyl5vuJ5ETQ",
"name": "Person"
},
"programStages":
[
{
"id": "A03MvHHogjR",
},
{
"id": "ZzYYXq4EJie",
},
{
"id": "AREMvHHogjR",
},
{
"id": "ZzYYXq4fJie",
}
]
},
{
"id": "IGRINAT79UW",
"created": "2013-03-04T10:41:07.494+0000",
"trackedEntity":
{
"id": "cyl5vuJ5ETQ",
"name": "Person"
},
"programStages":
[
{
"id": "A03MvHHogjR",
},
{
"id": "ZzYYXq4fJie",
},
{
"id": "A01MvHHogjR",
},
{
"id": "ZzGYXq4fJie",
}
]
}
]
}

One approach is simply to create POJOs for the various entities.
If you assume the following for TrackEntity
class TrackedEntity {
private final String id;
private final String name;
#JsonCreator
TrackedEntity(
#JsonProperty("id") final String id,
#JsonProperty("name") final String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
Then the following may be suitable for ProgramStage:
class ProgramStage {
private final String id;
#JsonCreator
ProgramStage(#JsonProperty("id") final String id) {
this.id = id;
}
public String getId() {
return id;
}
}
The Program class is slightly trickier since it must parse som kind of zoned date. I have used the Java 8 ZonedDateTime in this example with a custom formatter. You can also use JSR 310 module as described in this answer.
class Program {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxx");
private final ZonedDateTime created;
private final String id;
private final List<ProgramStage> programStages;
private final TrackedEntity trackedEntity;
#JsonCreator
public static Program of(
#JsonProperty("id") final String id,
#JsonProperty("created") final String created,
#JsonProperty("trackedEntity") final TrackedEntity trackedEntity,
#JsonProperty("programStages") final List<ProgramStage> programStages) {
return new Program(
id,
ZonedDateTime.parse(created, FORMATTER),
trackedEntity,
programStages);
}
public Program(
final String id,
final ZonedDateTime created,
final TrackedEntity trackedEntity,
final List<ProgramStage> programStages) {
this.id = id;
this.created = created;
this.trackedEntity = trackedEntity;
this.programStages = programStages;
}
public ZonedDateTime getCreated() {
return created;
}
public String getId() {
return id;
}
public List<ProgramStage> getProgramStages() {
return programStages;
}
public TrackedEntity getTrackedEntity() {
return trackedEntity;
}
}
Finally, to fix the outer programs entity the following can be used:
class Programs {
private final List<Program> programs;
#JsonCreator
Programs(#JsonProperty("programs") final List<Program> programs) {
this.programs = programs;
}
public List<Program> getPrograms() {
return programs;
}
}
To use the whole thing, simply instantiate an ObjectMapper and use the readValue method like this:
final Programs programs = new ObjectMapper().readValue(json, Programs.class);

Yes. You should be fine. Crate a data structure which represents your data:
public class Container
{
public List<ProgramInfo> programs {get;set;}
}
public class ProgramInfo
{
public string id{get; set;}
public DateTime created{get;set;}
public TrackEntity trrack{get;set;}
}
public class TrackEntity
{
public string id{get;set;}
public string name{get;set;}
}
//Then call the deserialise or serialize
Container container = new JavaScriptSerializer().Deserialize<Container>(yourString);

public class TrackedEntity
{
public string id { get; set; }
public string name { get; set; }
}
public class ProgramStage
{
public string id { get; set; }
}
public class Program
{
public string id { get; set; }
public string created { get; set; }
public TrackedEntity trackedEntity { get; set; }
public List<ProgramStage> programStages { get; set; }
}
public class RootObject
{
public List<Program> programs { get; set; }
}
//Then call the deserialise or serialize
var container = new JavaScriptSerializer().Deserialize<RootObject>(inputjson);
hope it works for you.

Related

Find values from list with key value pair

I want to find the key value pair from a list. I want to put the values into a parameter from another method. I have a deserialzied map that contains the list called "InfoFields" here is my json string:
{
"operation": "get-id",
"payload": {
"ApplicationContext": {
"Context": "JORDFEIL",
"SenderId": "xxx",
"subContext": ""
},
"supportIssue": {
"InfoFields": [
{
"Key": "key1",
"Value": "value1"
},
{
"Key": "key2",
"Value": "value2"
},
{
"Key": "key3",
"Value": "value3"
}
],
}
},
"type": "~:CustomerInquiry",
}
Here is my wrapper class aswell
public class WebskjemaModel {
public class ApplicationContext {
public String Context;
public String SenderId;
public String subContext;
}
public String operation;
public Payload payload;
public String type;
public class InfoFields {
public String Key;
public String Value;
}
public class Payload {
public ApplicationContext ApplicationContext;
public SupportIssue supportIssue;
}
public class SupportIssue {
public List<InfoFields> InfoFields;
public String assignId;
public String businessObjectType;
public String comment;
public String contactDate;
public String contactName;
public String customerName;
public String customerNo;
public String docClass;
public String format;
public String objectDescription;
public String objectId;
public String subject;
}
public static WebskjemaModel parse(String json) {
return (WebskjemaModel) System.JSON.deserialize(json, WebskjemaModel.class);
}
}
I have created a map that contains the deserialzed object, now i want to map the fields for a single case. Here is what i have tried, but i cant seem to get the values from the key and from the value:
public with sharing class WebskjemaCaseCreator {
#TestVisible
private Map<Id, Case> cases { get; set; }
#TestVisible
private Map<Id, WebskjemaModel> webskjemaModels = new Map<Id, WebskjemaModel>();
public WebskjemaCaseCreator(Map<Id, Case> cases) {
this.cases = cases;
deserializeJson();
mapFields();
}
private void deserializeJson() {
for (Id caseId : cases.keySet()) {
Case c = cases.get(caseId);
WebskjemaModel model = WebskjemaModel.parse(c.Webskjemablob__c);
webskjemaModels.put(caseId, model);
}
}
private void mapFields() {
for (Id caseId : cases.keySet()) {
Case c = cases.get(caseId);
WebskjemaModel model = webskjemaModels.get(caseId);
}
}
private void mapFieldsForSingleCase(Case c, WebskjemaModel model) {
String context = model.payload.ApplicationContext.Context;
List<WebskjemaModel.InfoFields> myinfoFields = model.payload.supportIssue.InfoFields;
for (WebskjemaModel.InfoFields fields : myinfoFields) {
mapInfoField(c, context, fields.get(key), fields.get(value));
}
// TODO: Find WebskjemaModel inforfields and loop
// for (list of infofields for this model only){
// TODO: call method mapInfoField
// }
}
private void mapInfoField(Case c, String context, String infoFieldKey, String infoFieldValue) {
WebskjemaMapping__mdt[] webskjemarecords = [
SELECT CaseField__c, Context__c, JsonField__c, IfsField__c
FROM WebskjemaMapping__mdt
];
// TODO: Find Case field from custom meta data type mapping, based on context and infoFielfKey
// TODO: Put value from inforFieldValue into Case field
}
}

De-serializing a JSON GET Request Response in Java

De-Serializing Objects within a List
I currently have an 'Admin' Entity in which upon a GET Request of AllAdmin() will return me the following response. This was used in Postman.
GET Response for AllAdmin() [POSTMAN]
[
{
"adminId": 1,
"fullName": "Patrick ",
"email": "patrick#gmail.com",
"dob": "1669-12-12",
"mobileNumber": "96369636",
"password": "password123!",
"usages": [
{
"id": 3,
"datetimeUnlocked": "2021-06-07 10:12:23"
},
{
"id": 4,
"datetimeUnlocked": "2021-06-07 10:12:27"
}
],
"authorization": [
{
"id": 2,
"datetimeAccepted": "2021-06-07 10:12:14"
}
],
"adminAllow": []
},
{
"adminId": 2,
"fullName": "Worker ",
"email": "worker#gmail.com",
"dob": "2000-12-12",
"mobileNumber": "96399639",
"password": "password123!",
"usages": [],
"authorization": [],
"adminAllow": []
} ]
The current code is my Admin Model in my Android Application.
Admin.java Model Class
public class Admin {
#SerializedName("adminId")
private long adminID;
#SerializedName("fullName")
private String adminFullName;
#SerializedName("email")
private String adminEmail;
#SerializedName("dob")
private String adminDOB;
#SerializedName("mobileNumber")
private String adminMobileNumber;
// Constructor
public Admin(long adminID, String adminFullName, String adminEmail, String adminDOB, String adminMobileNumber) {
this.adminID = adminID;
this.adminFullName = adminFullName;
this.adminEmail = adminEmail;
this.adminDOB = adminDOB;
this.adminMobileNumber = adminMobileNumber;
}
// Getter
public long getAdminID() {
return adminID;
}
public String getAdminFullName() {
return adminFullName;
}
public String getAdminEmail() {
return adminEmail;
}
public String getAdminDOB() {
return adminDOB;
}
public String getAdminMobileNumber() {
return adminMobileNumber;
}
}
I would like to clarify on how I would de-serialize the usages and authorization properties so that I am able to access and manipulate the data entries for these?
I thank you in advance for your clarifications!
For usages, you should use a List<Usage> as the serializable. My assumption is you are using Retrofit. So the Retrofit Gson converter will take care of parsing the array of Usage. The same logic would apply for authorization. You can use the different key there for the datetimeAccepted case.
Check this sample for Usage
public class Usage {
#SerializedName("id")
#Expose
private Integer id;
#SerializedName("datetimeUnlocked")
#Expose
private String datetimeUnlocked;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDatetimeUnlocked() {
return datetimeUnlocked;
}
public void setDatetimeUnlocked(String datetimeUnlocked) {
this.datetimeUnlocked = datetimeUnlocked;
}
}
Your updated Admin class must look like this
public class Admin {
#SerializedName("adminId")
private long adminID;
#SerializedName("fullName")
private String adminFullName;
#SerializedName("email")
private String adminEmail;
#SerializedName("dob")
private String adminDOB;
#SerializedName("mobileNumber")
private String adminMobileNumber;
#SerializedName("usages")
private List<Usage> usages;
// Constructor
public Admin(long adminID, String adminFullName, String adminEmail, String adminDOB, String adminMobileNumber, List<Usage> usages) {
this.adminID = adminID;
this.adminFullName = adminFullName;
this.adminEmail = adminEmail;
this.adminDOB = adminDOB;
this.adminMobileNumber = adminMobileNumber;
this.usages = usages;
}
// Getter
public long getAdminID() {
return adminID;
}
public String getAdminFullName() {
return adminFullName;
}
public String getAdminEmail() {
return adminEmail;
}
public String getAdminDOB() {
return adminDOB;
}
public String getAdminMobileNumber() {
return adminMobileNumber;
}
public List<Usage> getUsages() {
return usages;
}
}

Jackson mapping JSON to Java POJO - MismatchedInstanceException

I´m using spring rest template and want to parse the JSON response into the corresponding java POJO. I get every time a MismatchedInputException - Cannot deserialized instance of out of start array token.
I´ve looked at several questions relating to jackson but didn´t find the suitable solution.
I´m not sure if my POJO structure is correct.
First the related JSON:
{
"days":[
[
{
"day":"2019-04-29",
"time":"08:00-09:30",
"room":"room1",
"name":"lecture1",
"lecturer":"prof1",
"eventId":332713,
"modified":false
}
],
[
],
[
],
[
{
"day":"2019-05-02",
"time":"08:00-10:15",
"room":"room2",
"name":"lecture2",
"lecturer":"prof2",
"eventId":332714,
"modified":false
},
{
"day":"2019-05-02",
"time":"10:45-13:00",
"room":"room3",
"name":"lecture3",
"lecturer":"prof3",
"eventId":332721,
"modified":false
}
],
[
],
[
],
[
]
]
}
POJO Structure:
public class Main{
ArrayList<Day> days = new ArrayList<>();
public ArrayList<Day> getDays() {
return days;
}
public void setDays(ArrayList<Day> days) {
this.days = days;
}
public class Day {
private String day;
private String time;
private String room;
private String name;
private String lecturer;
private Integer eventId;
private Boolean modified;
public Day() {
}
// Getter and setter of Day Object
}
}
Rest-Template Method in API Object:
public String getInfoByEventsPeriods(String eventIDs) {
String url = APIURL + "/info?from="2019-04-29"&to="2019-05-03"&eventId=" + eventIDs;
return rest.getForObject(url, String.class);
}
Processing with ObjectMapper in Main Object:
public Main getLectures(String eventIDs) throws IOException {
API api = new API();
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(api.getInfoByEventsPeriods(eventIDs), Main.class);
}
Yes, you'r right,the wrong type is used while deserializing.
I'm not sure exactly, but your days array contains another arrays with day objects, but in your structure ArrayList consists of objects day, not array of them.
Correct JSON would be:
{
"days":[
{
"day":"2019-04-29",
"time":"08:00-09:30",
"room":"room1",
"name":"lecture1",
"lecturer":"prof1",
"eventId":332713,
"modified":false
},
{
"day":"2019-05-02",
"time":"08:00-10:15",
"room":"room2",
"name":"lecture2",
"lecturer":"prof2",
"eventId":332714,
"modified":false
},
......
]
}
So, change response structure or pojo object depending on what needs to be fixed.
If you need to modify POJO, your ArrayList must contains List of days.
Can you have your POJO like below. I tried to deserialize with your JSON and I was able to deserialize successfully.
class Day {
private String day;
private String time;
private String room;
private String name;
private String lecturer;
private Integer eventId;
private Boolean modified;
public void setDay(String day) {
this.day = day;
}
public void setTime(String time) {
this.time = time;
}
public void setRoom(String room) {
this.room = room;
}
public void setName(String name) {
this.name = name;
}
public void setLecturer(String lecturer) {
this.lecturer = lecturer;
}
public void setEventId(Integer eventId) {
this.eventId = eventId;
}
public void setModified(Boolean modified) {
this.modified = modified;
}
}
class Main{
private List<List<Day>> days;
public void setDays(List<List<Day>> days) {
this.days = days;
}
}

UnrecognizedPropertyException: Unrecognized field "sections"

I'm using Jackson as part of a spring boot app. I am turning JSON into Java, and I am getting this error. I did some research, but I still don't understand what is going wrong or how to fix it.
Here is the JSON fragment:
"dataBlock": {
"sections": [
{
"info": "",
"prompt": "",
"name": "First Section",
"sequence": 0,
"fields": [],
"gatingConditions": [],
"guid": "480d160c-c34f-4022-97b0-e8a1f28c49ae",
"id": -2
}
],
"prompt": "",
"id": -1,
"name": ""
}
So my Java object for this "dataBlock" element:
public class DataBlockObject {
private int id;
private String prompt;
private String name;
private List<SectionObject> sections;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SectionObject> getSections() {
return sections;
}
public void setSections(List<SectionObject> sections) {
this.sections = sections;
}
}
And the Section object is this:
public class SectionObject {
private int id;
private String name;
private String prompt;
private String info;
private int sequence;
private List<FieldObject> fields;
private List<GatingConditionObject> gatingConditions;
private String guid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public List<FieldObject> getFields() {
return fields;
}
public void setFields(List<FieldObject> fields) {
this.fields = fields;
}
public List<GatingConditionObject> getGatingConditions() {
return gatingConditions;
}
public void setGatingConditions(List<GatingConditionObject> gatingConditions) {
this.gatingConditions = gatingConditions;
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
}
So it seems to me that Jackson would make a DataBlockObject, map the obvious elemenets, and create an array that I have clearly marked as a List named sections. -- just like the JSON shows.
Now the error is:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "sections" (class com.gridunity.workflow.bean.json.SectionObject), not marked as ignorable (8 known properties: "gatingConditions", "sequence", "prompt", "fields", "id", "info", "guid", "name"])
Now according to that error it would seem that one of my 8 elements should be named "sections" - But that's not one of my elements. It clearly has a problem with my List of Sections, but I cant figure out what it is.
Can someone explain WHY this is happening, especially sence it looks like I have my structure correct, and how to fix this. I have seen this on other posts:
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
But that seems incredibly wrong as I know all of my properties.
It looks like the JSON itself has another sections field in one or more of the dataBlock.sections items. If you don't have control over the construction of the JSON object, you'll need to add a #JsonIgnoreProperties annotation on the SectionObject class so that when the JSON object has fields that aren't specified in the POJO, it won't throw an error during deserialization.
#JsonIgnoreProperties(ignoreUnknown = true)
public class SectionObject {
// class members and methods here
}

Jackson: vary JSON property value by view

Using Jackson, I know that I can include / exclude a property from serialization for a view by using #JsonView.
How can I vary a JSON property's value by view?
e.g., I might want a property's value in view A to be the whole object, in view B to be the object with certain properties filtered out, in view C, I just want it to be the "id" (no object), and in view D, I might want it to be the "name" (no object):
// view A JSON
{
"prop": {"id": 123, "name": "abc", "description": "def"}
}
// view B JSON
{
"prop": {"id": 123, "name": "abc"}
}
// view C JSON
{
"prop": 123
}
// view D JSON
{
"prop": "abc"
}
You can probably achieve this using generics but you also need to know in advance what concrete class to use, e.g.:
public static void main(String[] args) throws Exception {
final ObjectMapper mapper = new ObjectMapper();
final MyStuff<Prop> myStuff = mapper.readValue("{\"prop\": {\"id\": 123, \"name\": \"abc\", \"description\": \"def\"}}", MyStuff.class);
final MyStuff<String> myStuff1 = mapper.readValue("{\"prop\": \"abc\"}", MyStuff.class);
final MyStuff<Integer> myStuff2 = mapper.readValue("{\"prop\": 123}", MyStuff.class);
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Prop {
private Integer id;
private String name;
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class MyStuff<T> {
private T prop;
public T getProp() {
return prop;
}
public void setProp(T prop) {
this.prop = prop;
}
}
so not sure if this is what you want.

Categories

Resources