Deserialization using Jackson Json where one of the keys is dynamic - java

I am fairly new to Jackson. I am trying to map the following json to a POJO using Jackson for deserialization.
{
"data": [
{
"customerName": "abc",
"varaible_Key1": {
"p1": "text data",
"p2": "textarea data",
........
}
},
{
"customerName": "bbc",
"varaible_Key2": {
"p1": "text",
"p2": "textarea"
......
}
},
{
"customerName": "xyz",
"varaible_Key3": {
"p1": "xyz text",
"p2": "xyz textarea"
......
}
}
///////more customername / variable_keys
]
}
The problem I am facing is with dynamic / variable keys in the json.
I have tried using #JsonAnySetter in the POJO as shown below.
public class Foo {
#JsonProperty("customerName")
private String name;
private Map<String, DataObject> properties;
#JsonAnyGetter
public Map<String, DataObject> getProperties() {
return properties;
}
#JsonAnySetter
public void add(String key, DataObject value) {
properties.put(key, value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
where DataObject contains the fields like p1,p2 and so on.
public class DataObject {
#JsonProperty("p1")
private String firstValue;
#JsonProperty("p2")
private String secondValue;
#JsonProperty("p3")
private String thirdValue;
#JsonProperty("p4")
private String fourthValue;
public String getFirstValue() {
return firstValue;
}
public void setFirstValue(String firstValue) {
this.firstValue = firstValue;
}
public String getSecondValue() {
return secondValue;
}
public void setSecondValue(String secondValue) {
this.secondValue = secondValue;
}
public String getThirdValue() {
return thirdValue;
}
public void setThirdValue(String thirdValue) {
this.thirdValue = thirdValue;
}
public String getFourthValue() {
return fourthValue;
}
public void setFourthValue(String fourthValue) {
this.fourthValue = fourthValue;
}
}
I keep getting the below error. Any help on this is appreciated.
com.fasterxml.jackson.databind.JsonMappingException: N/A (through
reference chain:
com.epic.customer.dto.DataField["data"]->java.util.ArrayList[0]-> com.epic.customer.dto.Foo["varaible_Key1"])
at
com.fasterxml.jackson.databind.deser.SettableAnyProperty._throwAsIOE(SettableAnyProperty.java:214)
at
com.fasterxml.jackson.databind.deser.SettableAnyProperty.set(SettableAnyProperty.java:179)
at
com.fasterxml.jackson.databind.deser.SettableAnyProperty.deserializeAndSet(SettableAnyProperty.java:134)
at
com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1539)
at
com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
at
com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at
com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:285)
at
com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at
com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
at
com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at
com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:287)
at
com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at
com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)

You just need to create 2 setters, for specific property:
class Foo {
private DataObject dataObject;
public DataObject getDataObject() {
return dataObject;
}
public void setVaraible_Key1(DataObject dataObject) {
this.dataObject = dataObject;
}
public void setVaraible_Key2(DataObject dataObject) {
this.dataObject = dataObject;
}
}
Further usages refers here.

I think you have to give it a hint on the type of object you want back:
ObjectMapper mapper = new ObjectMapper();
Map<String, DataObject> test = mapper.readValue("Insert Data in here", mapper.getTypeFactory().constructMapLikeType(HashMap.class, String.class, DataObject.class));

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
}
}

How to map JSON to Java POJO with a dynamic field in JSON?

I would like create a Java object which map this JSON object:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"GBP": {
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
}
},
"status": "success"
}
Only the "GBP" property name is dynamic field, it could be another currency symbol next time like "USD", "JPY" etc.
I create the Java class like this:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"base_currency_code",
"base_currency_name",
"amount",
"updated_date",
"rates",
"status"
})
public class CurrencyConvertDto {
#JsonProperty("base_currency_code")
private String baseCurrencyCode;
#JsonProperty("base_currency_name")
private String baseCurrencyName;
#JsonProperty("amount")
private String amount;
#JsonProperty("updated_date")
private String updatedDate;
#JsonProperty("rates")
private Rates rates;
#JsonProperty("status")
private String status;
/*
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
*/
#JsonProperty("base_currency_code")
public String getBaseCurrencyCode() {
return baseCurrencyCode;
}
#JsonProperty("base_currency_code")
public void setBaseCurrencyCode(String baseCurrencyCode) {
this.baseCurrencyCode = baseCurrencyCode;
}
#JsonProperty("base_currency_name")
public String getBaseCurrencyName() {
return baseCurrencyName;
}
#JsonProperty("base_currency_name")
public void setBaseCurrencyName(String baseCurrencyName) {
this.baseCurrencyName = baseCurrencyName;
}
#JsonProperty("amount")
public String getAmount() {
return amount;
}
#JsonProperty("amount")
public void setAmount(String amount) {
this.amount = amount;
}
#JsonProperty("updated_date")
public String getUpdatedDate() {
return updatedDate;
}
#JsonProperty("updated_date")
public void setUpdatedDate(String updatedDate) {
this.updatedDate = updatedDate;
}
#JsonProperty("rates")
public Rates getRates() {
return rates;
}
#JsonProperty("rates")
public void setRates(Rates rates) {
this.rates = rates;
}
#JsonProperty("status")
public String getStatus() {
return status;
}
#JsonProperty("status")
public void setStatus(String status) {
this.status = status;
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Rates {
private List<Map<String, String>> rateInfo = new ArrayList<Map<String, String>>();
#JsonAnySetter
public void setDynamicProperty(String name, Map<String, String> map) {
rateInfo.add(map);
}
public List<Map<String, String>> getRateInfo() {
return rateInfo;
}
public void setRateInfo(List<Map<String, String>> rateInfo) {
this.rateInfo = rateInfo;
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"currency_name",
"rate",
"rate_for_amount"
})
public class RateInfo {
#JsonProperty("currency_name")
private String currencyName;
#JsonProperty("rate")
private String rate;
#JsonProperty("rate_for_amount")
private String rateForAmount;
/*
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
*/
#JsonProperty("currency_name")
public String getCurrencyName() {
return currencyName;
}
#JsonProperty("currency_name")
public void setCurrencyName(String currencyName) {
this.currencyName = currencyName;
}
#JsonProperty("rate")
public String getRate() {
return rate;
}
#JsonProperty("rate")
public void setRate(String rate) {
this.rate = rate;
}
#JsonProperty("rate_for_amount")
public String getRateForAmount() {
return rateForAmount;
}
#JsonProperty("rate_for_amount")
public void setRateForAmount(String rateForAmount) {
this.rateForAmount = rateForAmount;
}
}
But when compile, it seems have problem. it can't map the JSON object with the dynamic field . Does anyone know how to fix it? Thanks you very much.
You can use #JsonAnyGetter and get the additional dynamic key value pair(s) in a Map. Please refer to the usage of #JsonAnyGetter in the following example and do let me know if you still need help.
https://www.logicbig.com/tutorials/misc/jackson/jackson-any-setter.html
If you are able,
the simplest option would be to use a reasonable JSON design.
Here is an example:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"currentySymbol": "GBP",
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
}
},
"status": "success"
}
May be you were thinking too complicated here.
In your CurrencyConvertDto class, instead of using
#JsonProperty("rates")
private Rates rates;
you can simply use
#JsonProperty("rates")
private Map<String, RateInfo> rates;
(and of course adjust the getRatesand setRates methods accordingly).
And then you don't need the Rates class anymore.
Jackson can cope with this out-of-the-box.
It will handle arbitrary currency codes as keys of the map, like in:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"GBP": {
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
},
"EUR": {
"currency_name": "Euro",
"rate": "0.120",
"rate_for_amount": "18.07"
}
},
"status": "success"
}
And by the way: You don't need to repeat the #JsonProperty
annotations on the getter and setter methods. Putting #JsonProperty
only on the member variables is already enough.

Jackson, getting null return at List<>

I don't understand why returns a null?
Have 2 classes with Jackson and String Volley request
StringRequest request = new StringRequest(Request.Method.GET,regions, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG,"response"+ response);
try {
ObjectMapper mapper=new ObjectMapper();
Pojo pojo=mapper.readValue(response,Pojo.class);
System.out.println(pojo.toString());
}catch (IOException e) {
e.printStackTrace();
}
Json
{
"success": true,
"data": [
{
"id": "1",
"name": "RegionABC"
},
{
"id": "2",
"name": "RegionDEF"
},
]
}
It parses by 2 classes:
Pojo
public class Pojo {
public Pojo(){}
private boolean success;
private List<Pojo> data;
#JsonProperty("data")
public List<Pojo> getDataPojo() {
return data;
}
public void setDataPojo(List<Pojo> data) {
this.data = data;
}
// getters & setters
#JsonProperty("success")
public boolean isSuccessPojo() {
return success;
}
public void setSuccessPojo(boolean success) {
this.success = success;
}
#Override
public String toString() {
return "data "+data;
}
and DataPojo
public class DataPojo {
private String id, name;
public String getIdDataPojo() {
return id;
}
public void setIdDataPojo(String id) {
this.id = id;
}
public String getNameDataPojo() {
return name;
}
public void setNameDataPojo(String name) {
this.name = name;
}
Where is i have an incorrect code? It works only with simple strings, but with List<> - no.
find this part in your code:
#JsonProperty("data")
public List<Pojo> getDataPojo() {
return data;
}
public void setDataPojo(List<Pojo> data) {
this.data = data;
}
and replace by this code:
#JsonProperty("data")
public List<DataPojo> getDataPojo() {
return data;
}
public void setDataPojo(List<DataPojo> data) {
this.data = data;
}

Android java/gson serialization List<MyObject> produces null,null,null

I have following classes:
public class MyProperty
{
public String Key;
public String Value;
}
public class MyModel
{
public String Name;
public List<MyProperty> Properties;
}
When I try to serialize an object of type MyObject like this:
MyModel m = new MyModel(){{
Name="aaaa";
Properties = new ArrayList<MyProperty>();
}};
m.Properties = new ArrayList<MyProperty>();
m.Properties.add(new MyProperty() {{ Key="a"; Value="1"; }});
m.Properties.add(new MyProperty() {{ Key="b"; Value="11"; }});
m.Properties.add(new MyProperty() {{ Key="c"; Value="111"; }});
String json1 = g.toJson(m, MyModel.class);
I'm getting following result:
{"Name":"aaaa","Properties":[null,null,null]}
Why is the list of properties serialized to list of null's when the source objects are definitely not null?
Deserialization of a string
{"Name":"aaaa","Properties":[{"Key":"a","Value":"1" etc }]}
works fine.
The problem you're likely hitting is polymorphism - you're model says that the Properties are of type "MyProperty" but your code fragment uses "SyncProperty". There are some gotchas to doing this with Gson - have a look at the discussion here: How to handle deserializing with polymorphism?
I will tell you why this code has the output you are looking for and not the code you have in question.
Code
import java.util.ArrayList;
import com.google.gson.Gson;
public class TestOneDrive {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyModel model = new MyModel();
model.setName("someName");
ArrayList<MyProperty> myProperties = new ArrayList<>();
for (int i = 0; i < 5; i++) {
MyProperty myProperty = new MyProperty();
myProperty.setKey("Key_" + i);
myProperty.setValue("Value_" + i);
myProperties.add(myProperty);
}
model.setProperties(myProperties);
String result = (new Gson()).toJson(model);
System.out.println("" + result);
}
}
class MyProperty {
public String Key;
public String Value;
public String getKey() {
return Key;
}
public void setKey(String key) {
Key = key;
}
public String getValue() {
return Value;
}
public void setValue(String value) {
Value = value;
}
}
class MyModel {
public String Name;
public ArrayList<MyProperty> Properties;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public ArrayList<MyProperty> getProperties() {
return Properties;
}
public void setProperties(ArrayList<MyProperty> properties) {
Properties = properties;
}
}
Output
{
"Name": "someName",
"Properties": [
{
"Key": "Key_0",
"Value": "Value_0"
},
{
"Key": "Key_1",
"Value": "Value_1"
},
{
"Key": "Key_2",
"Value": "Value_2"
},
{
"Key": "Key_3",
"Value": "Value_3"
},
{
"Key": "Key_4",
"Value": "Value_4"
}
]
}

Binding json, that has a list, with an object using Jackson

Given I have the following json:
{
"Company": {
"name": "cookieltd",
"type": "food",
"franchise_location": [
{
"location_type": "town",
"address_1": "5street"
},
{
"location_type": "village",
"address_1": "2road"
}
]
}
}
How can it be binded to the following object classes using Jackson?:
1) Company class
public class Company
{
String name, type;
List<Location> franchise_location = new ArrayList<Location>();
[getters and setters]
}
2) Location class
public class Location
{
String location_type, address_1;
[getters and setters]
}
I have done:
String content = [json above];
ObjectReader reader = mapper.reader(Company.class).withRootName("Company"); //read after the root name
Company company = reader.readValue(content);
but I am getting:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "franchise_location"
As far as I can tell, you are simply missing an appropriately named getter for the field franchise_location. It should be
public List<Location> getFranchise_location() {
return franchise_location;
}
(and the setter)
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
Alternatively, you can annotate your current getter or field with
#JsonProperty("franchise_location")
private List<Location> franchiseLocation = ...;
which helps to map JSON element names that don't really work with Java field name conventions.
The following works for me
public static void main(String[] args) throws Exception {
String json = "{ \"Company\": { \"name\": \"cookieltd\", \"type\": \"food\", \"franchise_location\": [ { \"location_type\": \"town\", \"address_1\": \"5street\" }, { \"location_type\": \"village\", \"address_1\": \"2road\" } ] } }";
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(Company.class).withRootName(
"Company"); // read after the root name
Company company = reader.readValue(json);
System.out.println(company.getFranchise_location().get(0).getAddress_1());
}
public static class Company {
private String name;
private String type;
private List<Location> franchise_location = new ArrayList<Location>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Location> getFranchise_location() {
return franchise_location;
}
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
}
public static class Location {
private String location_type;
private String address_1;
public String getLocation_type() {
return location_type;
}
public void setLocation_type(String location_type) {
this.location_type = location_type;
}
public String getAddress_1() {
return address_1;
}
public void setAddress_1(String address_1) {
this.address_1 = address_1;
}
}
and prints
5street
my solution for JSON is always GSON, you can do some research on that, as long as you have the correct structure of class according to the JSON, it can automatically transfer from JSON to object:
Company company = gson.fromJson(json, Company.class);
GSON is so smart to do the convertion thing!
enjoy GSON !

Categories

Resources