Jackson parse different level fields to one java class - java

I have the following JSON string and java class.
Json:
{
"records": [
{
"metadata": {
"id": "1129836408668",
"partition": "ecp:9-ca69437a-2ad5-4233-bfc4-fd9aa049569d",
"version": "ecp:9-f66cf504-ba65-48a9-91c0-d1d8229e4899",
"currentAt": "2021-12-07T11:13:50.699Z"
},
"state": {
"effectiveFrom": "2021-12-14T06:35:09.063Z",
"effectiveTo": "2021-12-14T06:35:09.063Z",
"currentFrom": "2021-12-14T06:35:09.063Z",
"currentTo": "2021-12-14T06:35:09.063Z",
"dataItem": {
"ModifiedDate": "2021-12-07T11:13:44.099Z",
"ObjectId": "1129836408668",
"ChangeFlag": "",
"IsUnconfirmed": "false",
"CreatedDate": "2021-12-07T11:13:44.099Z"
}
}
}
],
"index": null
}
Class A:
public class ParentClass {
private Date effectiveFrom;
private Date effectiveTo;
private Date currentFrom;
private Date currentTo;
private String id;
}
Class B
public class ChildClass extends ParentClass {
#JsonProperty("ModifiedDate")
private Date ModifiedDate;
#JsonProperty("ObjectId")
private String ObjectId;
#JsonProperty("ChangeFlag")
private String ChangeFlag;
#JsonProperty("IsUnconfirmed")
private String IsUnconfirmed;
#JsonProperty("CreatedDate")
private Date CreatedDate;
}
And I'd like to parse the values in state jsonnode to ChildClass wiht Jackson. Which way is the best way to do it.
My thoughts is trying to parse state into MapA, and then parse dateItem into MapB, then merge MapB to MapA, you will get a Mapc, and then parse MapC to ChildClass. However, I think this is not a efficiency way to do it. So will you guys have a better solution.
MapC:
{
effectiveFrom=2021-12-14T06:35:09.063Z,
effectiveTo=2021-12-14T06:35:09.063Z,
currentFrom=2021-12-14T06:35:09.063Z,
currentTo=2021-12-14T06:35:09.063Z,
ModifiedDate=2021-12-07T11:13:44.099Z,
ObjectId=1129836408668,
ChangeFlag=, IsUnconfirmed=false,
CreatedDate=2021-12-07T11:13:44.099Z
}

You don't actually need two classes. You need just one that is capable of deserializing dataItem block in a customized way. Something along the following lines should work:
public class ParentClass {
private Date effectiveFrom;
private Date effectiveTo;
private Date currentFrom;
private Date currentTo;
private String id;
private Date modifiedDate;
private String objectId;
private String changeFlag;
private String isUnconfirmed;
private Date createdDate;
#JsonProperty("dataItem")
private void unpackNested(Map<String, Object> dataItem) {
this.modifiedDate = Date.from(Instant.from(DateTimeFormatter.ISO_INSTANT.parse((String) dataItem.get("ModifiedDate"))));
this.objectId = (String) dataItem.get("ObjectId");
this.changeFlag = (String) dataItem.get("ChangeFlag");
this.isUnconfirmed = (String) dataItem.get("IsUnconfirmed");
this.createdDate = Date.from(Instant.from(DateTimeFormatter.ISO_INSTANT.parse((String) dataItem.get("CreatedDate"))));
}
}
You can find other options at https://www.baeldung.com/jackson-nested-values.

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

Parsing of json response from REST API which has id as field name

I want to parse the json string and form a pojo object but the response is somewhat unusual.
I have folloing type of response from API
"data": {
"12": {
"value": "$0.00",
"order_id": "12",
"order_date": "2020-08-26 15:50:05",
"category_name": "Games",
"brand_id": "4",
"denomination_name": "AED 50",
"order_quantity": "1",
"vendor_order_id": "A-123",
"vendor_location": "",
"vouchers": {
"804873": {
"pin_code": "41110AE",
"serial_number": "fddfgfgf1234444"
}
}
},
"15": {
"value": "$0.00",
"order_id": "15",
"order_date": "2020-08-26 08:39:11",
"category_name": "Games",
"brand_id": "52",
"brand_name": "PlayStation",
"denomination_name": "$20",
"order_quantity": "1",
"vendor_order_id": "A-316",
"vendor_location": "",
"vouchers": {
"806328": {
"pin_code": "fdfd",
"serial_number": "fawwwww"
}
}
}
}
}
How do I parse this response since inside data the field name is order id same with voucher
If you use Jackson JSON library, you should have POJOs like those shown below and use PropertyNamingStrategy.SnakeCaseStrategy to handle property names in the input JSON:
// top-level container
public class Response {
private Map<Integer, Order> data;
// getter/setter
}
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Order {
private String value; // may be some Currency class
private Integer orderId;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime orderDate;
private String categoryName;
private Integer brandId;
private String brandName;
private String denominationName; // may be Currency too
private Integer orderQuantity;
private String vendorOrderId;
private String vendorLocation;
private Map<Integer, Voucher> vouchers;
// getters/setters
}
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Voucher {
private String pinCode;
private String serialNumber;
// getters/setters
}

How to set JsonProperty value dynamically

I want to create a json request like below:
"additionalData": {
"riskdata.basket.item1.sku": "045155",
"riskdata.basket.item1.quantity": "1"
"riskdata.basket.item2.sku": "0451166",
"riskdata.basket.item2.quantity": "1"
...
"riskdata.basket.item4.sku": "0451111",
"riskdata.basket.item4.quantity": "2"
Please suggest how to set the JsonProperty value dynamically in the object mapping.
Example: deliveryMethod is a constant field hence I am able to map like below using JsonProperty annotation. However, how I can use the JsonProperty for sku and quantity so that it will accept as many number as possible. Any suggestion would be helpful.
public class AdditionalData implements java.io.Serializable
{
#JsonProperty(value = "riskdata.deliveryMethod")
private String deliveryMethod;
#JsonProperty(value = "riskdata.basket.item??.sku")
private String sku;
#JsonProperty(value = "riskdata.basket.item??.quantity")
private String quantity;
}
You can create a basket[] array property in your AdditionalDataclass.
public class AdditionalData implements java.io.Serializable
{
#JsonProperty(value = "riskdata.deliveryMethod")
private String deliveryMethod;
#JsonProperty(value = "riskdata.basket")
private Basket[] basket;
}
public class Basket implements java.io.Serializable
{
#JsonProperty(value = "sku")
private String sku;
#JsonProperty(value = "quantity")
private String quantity;
}
And the change your json structure like this:
"additionalData": {
"riskdata.basket": [
{
"sku": "045155",
"quantity": 1"
},
{
"sku": "045156",
"quantity": 1"
}]
}

Convert JSON to a Java object with a calculated field

I have this JSON object:
[
{
"field1": "xxxxx",
"field2": "vvvvvv",
"field3": "cccccc",
"field4": "zzzzzzz"
},
{
"field1": "aaaaa",
"field2": "ssssss",
"field3": "dddddd",
"field4": "ffffff"
}
]
I'm using FasterXML's Jackson library to deserialize this JSON to my class Foo. This one has this structure:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Foo {
private String id;
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field4")
private String customField4;
................
}
I would like to calculate value of field id at deserialize time. This value is the result of concatenating customField4 with customField3. Is possible to perform this kind of operation or do I need to pass this value into my JSON?
Ok guys, solution is to set a custom
#JsonDeserialize(using = EntityJsonDeserializerCustom.class)
in this way I've created a generic static class with only fields returned by json an then I override deserialize method to return me my object with calculated field
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonDeserialize(using = EntityJsonDeserializerCustom.class)
public class Foo {
private String id;
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field4")
private String customField4;
................
}
public class EntityJsonDeserializerCustom extends JsonDeserializer<Foo> {
#Override
public Foo deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
InnerFoo innerFoo = jp.readValueAs(InnerFoo.class);
Foo foo = new Foo();
foo.setField1(innerFoo.field1);
foo.setField2(innerFoo.field2);
foo.setField3(innerFoo.field3);
foo.setField4(innerFoo.field4);
foo.setId(innerFoo.field4 + innerFoo.field3);
return foo;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class InnerFoo {
#JsonProperty("field1")
private String customField1;
#JsonProperty("field2")
private String customField2;
#JsonProperty("field3")
private String customField3;
#JsonProperty("field3")
private String customField4;
}
}
In this way I solve my problem, I hope this is helpfully for community :D

Gson to POJO, how to read the inner JSON Objects as part of the same POJO?

I have a JSON like below and would like to convert it using Gson to one POJO.
I am trying to figure out how to basically cut down the inner nested objects like desc to be treated as part of the same Java object, instead of creating a new POJO named Desc.
Is there a way to do this with Serialized Names to look into nested JSON objects?
Thanks in advance!
JSON to be converted to POJO
{
'name': name,
'desc': {
'country': country,
'city': city,
'postal': postal,
'street': street,
'substreet': substreet,
'year': year,
'sqm': sqm
},
'owner': [owner],
'manager': [manager],
'lease': {
'leasee': [
{
'userId': leaseeId,
'start': leaseeStart,
'end': leaseeeEnd
}
],
'expire': leaseExpire,
'percentIncrease': leasePercentIncrease,
'dueDate': dueDate
},
'deposit': {
'bank': 'Sample Bank',
'description': 'This is a bank'
}
}
Custom POJO
public class Asset {
private String mId;
#SerializedName("name")
private String mName;
private String mCountry;
private String mCity;
private String mPostal;
private String mStreet;
private String mSubstreet;
private int mYear;
private int mSqm;
#SerializedName("owner")
private List<String> mOwners;
#SerializedName("manager")
private List<String> mManagers;
private List<Leasee> mLeasees;
private DateTime mLeaseExpiration;
private int mPercentIncrease;
private int mDueDate;
private String mDepositBank;
private String mDepositDescription;
}

Categories

Resources