How to handle multiple json fields in gson - java

I want to parse a json to an object which gives me details of every entity attached to a bank.
My json looks like :
{
"href" : "abc",
"configurations" :
[
{
"type" : "bank-customer",
"properties" : {
"cust-name" : "foo",
"acc-no" : "12345"
}
},
{
"type" : "bank-employee",
"properties" : {
"empl-name" : "foo",
"empl-no" : "12345"
}
}
]
}
The properties for various entity "type" is different.
Creating a pojo for this is the challenge. My properties.java will have to include all properties irrespective of the type of the property :
public class Configurations {
#SerializedName("type")
#Expose
private String entityType;
#SerializedName("properties")
#Expose
private Properties properties;
}
public class Properties {
#SerializedName("cust-name")
#Expose
private String custName;
#SerializedName("empl-no")
#Expose
private String emplNo;
#SerializedName("empl-name")
#Expose
private String emplName;
#SerializedName("acc-no")
#Expose
private String accNo;
}
This is painful when I have a lot of entity types and property per entity type. Is there any other way I can parse this json into different property objects for different entity types? I am using gson to parse the JSON
Note : I can't make any changes to the json itself.

I completely agree with mauros answer.
But you can also create a interface hierarchy and implement Instance Creator.

Its can be easily solved by using alternate keyword from #SerializedName annotation.
public class Properties {
#SerializedName(value="cust-name", alternate={"empl-name", "user-name"})
#Expose
private String name;
#SerializedName("acc-no", alternate={"empl-no", "user-id"})
#Expose
private String id;
//setter and getters
}

Related

Using JAVA Reflection how to create custom JSON object mapping

I have a json Object in the below format, I need to assign the values from Json to java object, But the label name in JSON and class is different.
{
FirstName: "Sample",
LastName: "LName",
Address: [
{
type: "Temp",
street: "test stree",
},
{
type: "Perm",
street: "test stree",
}
]
}
Class Parent{
private String Name1;
private String Nama2;
private List<Address> address;}
Class Address{
Private String type;
private String data;
}
I wanted to implement the custom object mapper using Java reflection. the mapping is as below, But I am not getting idea to implement this, Any valuable suggestion or usage of external api would help me to achieve the scenario.
Json Object name Jave Class object Name
FirstName ---------- Name1
LastName ---------- Name2
Address.type ------- Address class type
Address.street ----- Address class data
You would need reflexion if you receive json data with same structure with properties names changing, for example :
{
FirstName1: "Sample",
LastName1: "LName",
Address1: [
{
type1: "Temp",
street1: "test stree",
},
{
type1: "Perm",
street1: "test stree",
}
]
}
{
FirstName2: "Sample",
LastName2: "LName",
Address1: [
{
type2: "Temp",
street2: "test stree",
},
{
type2: "Perm",
street2: "test stree",
}
]
}
In your case, it rather look like a property name matching issue, you can annotate your java pojo like that :
public class Parent{
#JsonProperty("FirstName")
private String Name1;
#JsonProperty("LastName")
private String Nama2;
private List<Address> address;
}
public class Address{
private String type;
#JsonPRoperty("street")
private String data;
}
Finally you can deserialize your json object using standard Jackson library :
new ObjectMapper().readValue(json, Parent.class);

Create a generic JSONPatch object

I want to create a DTO for Jackson to serialize my jsonPatch variables into a json patch variable.
I have this attributes to represent the class Person:
#JsonProperty("lastName")
private String lastName;
#JsonProperty("firstName")
private String firstName;
#JsonProperty("age")
private int age;
#JsonProperty("a")
private A classA;
And also the class A has:
#JsonProperty("colour")
private String colour;
#JsonProperty("quantity")
private BigDecimal quantity;
The DTO that I created was something like this:
#JsonProperty("path")
protected String path;
#JsonProperty("op")
protected String operation;
#JsonProperty("value")
protected Object values;
The json patch that I receive can have on the values class either a Person or the class A:
Patch for Person:
[
{
"value":
{
"lastName": "Doe",
"firsName": "John"
},
"path": /people",
"op": "replace"
}
]
Patch for A:
[
{
"value":
{
"quantity": 12.1,
},
"path": /A",
"op": "remove"
}
]
The value can be either a Person or a class A, as I told you, that's why the value is an Object class.
My problem is that when Jackson serializes the value from Person, it serializes to a Map, and when the value is from object A, it serializes to a double, which will lead to casting the values, which could be very dangerous.
Is there any way to tell Jackson that the values are from a Person object or from an A object, so it can serialize without any cast?
I would recommend creating a common class with all the properties and add JsonIgnoreProperties to the class so that Jackson will ignore unknown properties while deserializing
#JsonIgnoreProperties(ignoreUnknown = true)
class Common {
#JsonProperty("colour")
private String colour;
#JsonProperty("quantity")
private BigDecimal quantity;
#JsonProperty("lastName")
private String lastName;
#JsonProperty("firstName")
private String firstName;
#JsonProperty("age")
private int age;
#JsonProperty("a")
private A classA;
}
And then after deserializing the JSON to Common object just do null check to identify deserialized JSON is Person or A class
if(common.getFirstName()!=null && common.getLastName()!=null)
// Person class
if(common.getQuantity()!=null)
// A class

How to deserialize complex JSON to java object

I need to deserialize JSON to java class.
I have JSON like the following:
{
"data": {
"text": "John"
},
"fields":[
{
"id": "testId",
"name": "fieldName",
"options": {
"color": "#000000",
"required": true
}
},
{
"id": "testId",
"name": "fieldName1",
"options": {
"color": "#000000",
"required": false
}
}
]
}
and I need to deserialize this JSON (only "fields" section) to java class like the following:
public class Field {
public final String id;
public final String name;
public final String color;
public final boolean required;
}
and I need to get something like the following:
// The key is the id from field object (it can be the same in the multiple objects.)
Map<String, List<Field>> fields = objectMapper.readValue(json, Map<String, List<Field>>);
How can I do it using Jackson?
As long as jackson doesn't support #JsonWrapped, you have to use the following work around.
First you need to create a custom class which contains the fields:
public class Fields {
public List<Field> fields;
}
Depending on your ObjectMapper configuration you have to add #JsonIgnoreProperties(ignoreUnknown = true) to the Fields class, to ignore any other properties.
Next is that you have to define the nested Options class which is solely used temporarily:
public class Options {
public String color;
public boolean required;
}
And at last add this constructor to your Field class:
#JsonCreator
public Field(#JsonProperty("id") String id, #JsonProperty("name") String name, #JsonProperty("options") Options options){
this.id = id;
this.name = name;
this.color = options.color;
this.required = options.required;
}
The #JsonCreator annotation indicates to jackson that this constructor needs to be used for the deserialization. Also the #JsonProperty annotations are required as arguments to constructors and methods are not preserved in the bytecode
Then you can deserialize your json just like this:
List<Field> fields = objectMapper.readValue(json, Fields.class).fields;

One POJO but the different XmlRootElement name

For Example, I have one POJO as shown below but it feeds into multiple operations but I do not want to create several identical POJO's just because the root element name changes for each operation. Hence I need one POJO but in such a way that I can dynamically change the Root Element Name.
#ToString
#MappedSuperclass
#lombok.Data
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#EqualsAndHashCode(callSuper = false)
public class AmountOrAccountBlockOrUnblockRequest extends XmlBuilder implements SessionGenerator {
#JsonIgnore
#XmlElement
private String TargetBankVerificationNumber;
#JsonIgnore
#XmlElement
private String Narration;
#JsonProperty("amount")
#XmlElement(name = "Amount")
private String amount;
#JsonProperty("savingAccountNumber")
#XmlElement(name = "TargetAccountNumber")
private String targetAccountNumber;
#JsonIgnore
#XmlElement
private String ChannelCode;
#JsonProperty("unblockId")
#JsonIgnore
#XmlElement
private String ReferenceCode;
#JsonIgnore
#XmlElement
private String DestinationInstitutionCode;
#JsonIgnore
#XmlElement
private String TargetAccountName;
#XmlElement
private String SessionID;
#JsonIgnore
#XmlElement
private String ReasonCode;
// if account is still blocked or released
#JsonProperty("block")
private boolean blockUnblock;
#JsonProperty("blockUnblockReason")
private String blockUnblockReason;
#Override
public String toXmlString() {
return super.convertObjectToXmlString(this, this.getClass());
}
#Override
public void generateSessionID(HelperFacade helperFacade) {
setSessionID(helperFacade.generateSessionID(this.getDestinationInstitutionCode()));
}
}
This single POJO above will serve several operations but with a different Root Element Name for each operation for example,
<AmountUnblockRequest>
<SessionID>000001100913103301000000000001</SessionID>
<DestinationInstitutionCode>000002</DestinationInstitutionCode>
<ChannelCode>7</ChannelCode>
<ReferenceCode>xxxxxxxxxxxxxxx</ReferenceCode>
<TargetAccountName>Ajibade Oluwasegun</TargetAccountName>
<TargetBankVerificationNumber>1033000442</TargetBankVerificationNumber>
<TargetAccountNumber>2222002345</TargetAccountNumber>
<ReasonCode>0001</ReasonCode>
<Narration>Transfer from 000002 to 0YY</Narration>
<Amount>1000.00</Amount>
</AmountUnblockRequest>
and
<AmountBlockRequest>
<SessionID>000001100913103301000000000001</SessionID>
<DestinationInstitutionCode>000002</DestinationInstitutionCode>
<ChannelCode>7</ChannelCode>
<ReferenceCode>xxxxxxxxxxxxxxx</ReferenceCode>
<TargetAccountName>Ajibade Oluwasegun</TargetAccountName>
<TargetBankVerificationNumber>1033000442</TargetBankVerificationNumber>
<TargetAccountNumber>2222002345</TargetAccountNumber>
<ReasonCode>0001</ReasonCode>
<Narration>Transfer from 000002 to 0YY</Narration>
<Amount>1000.00</Amount>
</AmountBlockRequest>
I want to avoid the pain of having to create two identical classes all because the root element name will change.
You can use Declarative Stream Mapping (DSM) stream parsing library. You don't need to annotate your POJO both for XML and JSON.
You just define the mapping for data you want to extract from XML.
Here are mapping definitions for your XML.
result:
path: /(AmountBlockRequest|AmountUnblockRequest) // path is regex
type: object
fields:
TargetBankVerificationNumber:
Narration:
amount:
path: amount
xml:
path: Amount
targetAccountNumber:
path: targetAccountNumber
xml:
path: TargetAccountNumber
channelCode:
path: ChannelCode
referenceCode:
path: ReferenceCode
destinationInstitutionCode:
path: DestinationInstitutionCode
targetAccountName:
path: TargetAccountName
sessionID:
path: SessionID
reasonCode:
path: ReasonCode
blockUnblockReason:
path: BlockUnblockReason
Java Code to parse XML:
DSM dsm=new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML)..create(AmountOrAccountBlockOrUnblockRequest.class);;
// For json
//DSM dsm=new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.JSON)..create(AmountOrAccountBlockOrUnblockRequest.class);
AmountOrAccountBlockOrUnblockRequest result= (AmountOrAccountBlockOrUnblockRequest)dsm.toObject(xmlFileContent);
// json represntation fo result
dsm.getObjectMapper().writerWithDefaultPrettyPrinter().writeValue(System.out, object);
Here is output:
{
"amount" : "1000.00",
"targetAccountNumber" : "2222002345",
"blockUnblock" : false,
"blockUnblockReason" : null,
"sessionID" : "000001100913103301000000000001",
"reasonCode" : "0001",
"referenceCode" : "xxxxxxxxxxxxxxx",
"channelCode" : "7",
"narration" : null,
"targetBankVerificationNumber" : null,
"destinationInstitutionCode" : "000002",
"targetAccountName" : "Ajibade Oluwasegun"
}
Currently it is not support serialization.

Serialize only the data you need from json

I get some data form JSON. I can't change JSON, it is foreign API. JSON...
"apply" : {
"type" : "submit",
"form" : "#pageForm",
"data" : {
"ctl00$MainContentArea$fld_offerCode" : "%promo"
},
"submit" : "#OfferCodeSubmit",
"response" : {
"type" : "html",
"total" : "#orderItemsDisplay .totals:last"
}
},
or
"apply" : {
"type" : "post",
"url" : "https:\/\/www.4wheelparts.com\/shoppingcart.aspx",
"submit" : "#ctl00_ContentPlaceHolder1_btnCouponCode",
"contentType" : "application\/x-www-form-urlencoded",
"data" : {
"__VIEWSTATE" : "",
"ctl00$ContentPlaceHolder1$tbCouponCode" : "%promo",
"ctl00$ContentPlaceHolder1$btnCouponCode" : "Redeem"
}
}
I want save JSON to database, and use "serialize data". But parameter "data" constantly changing. How can I serialize parameter "type", "url", "submit", and is't serialize parametr "data"?
I want to add to my DB this form...
"type" : "post"
"url" : "https:\/\/www.4wheelparts.com\/shoppingcart.aspx"
"data" : {
"__VIEWSTATE" : "",
"ctl00$ContentPlaceHolder1$tbCouponCode" : "%promo",
"ctl00$ContentPlaceHolder1$btnCouponCode" : "Redeem"
}
So I serialize the data...
public class Apply
{
#SerializedName("type")
#Expose
private String type;
#SerializedName("submit")
#Expose
private String submit;
#SerializedName("timeout")
#Expose
private Long timeout;
....How data should look like???
Or am I going to need to move the another way?
dont add data in your model and that will be ok. i suggest you to use Gson library. and do as below :
Apply apply = (new Gson()).fromJson(jsonString);
jsonString is a string variable containing your json.
you can import Gson library by adding this to your gradle file:
compile 'com.google.code.gson:gson:2.8.0'
If you need only few specific data,Can follow this
jsonObj = new JSONObject(strRequestPacket);
requiredData = jsonObj.getString("requiredData");
If you need to map to your entity class,then follow this
Gson gson = new Gson();
if(mob_farmerStatus !=null){
User user = gson.fromJson(strRequestPacket, User.class);
System.out.println("user:::"+user);
public class Apply
{
#SerializedName("type")
#Expose
private String type;
#SerializedName("submit")
#Expose
private String submit;
#SerializedName("timeout")
#Expose
private Long timeout;
#SerializedName("timeout")
#Expose
private Data data;
// Setter Getters here
}
public class Data
{
private String vIEWSTATE;
private String ctl00$ContentPlaceHolder1$tbCouponCode;
private String ctl00$ContentPlaceHolder1$btnCouponCode;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getVIEWSTATE() {
return vIEWSTATE;
}
public void setVIEWSTATE(String vIEWSTATE) {
this.vIEWSTATE = vIEWSTATE;
}
public String getCtl00$ContentPlaceHolder1$tbCouponCode() {
return ctl00$ContentPlaceHolder1$tbCouponCode;
}
public void setCtl00$ContentPlaceHolder1$tbCouponCode(String ctl00$ContentPlaceHolder1$tbCouponCode) {
this.ctl00$ContentPlaceHolder1$tbCouponCode = ctl00$ContentPlaceHolder1$tbCouponCode;
}
public String getCtl00$ContentPlaceHolder1$btnCouponCode() {
return ctl00$ContentPlaceHolder1$btnCouponCode;
}
public void setCtl00$ContentPlaceHolder1$btnCouponCode(String ctl00$ContentPlaceHolder1$btnCouponCode) {
this.ctl00$ContentPlaceHolder1$btnCouponCode = ctl00$ContentPlaceHolder1$btnCouponCode;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
I solved it problem with help:
import okhttp3.ResponseBody;
public interface ConfigsBodyRequest
{
#GET("config.json")
Observable<ResponseBody> getResponse();
}
I get json without serialization, and than parse json
//item - (String) content of json file.
JSONObject jsonObject = new JSONObject(item);
String required = jsonObject.getString("apply");
And now required is equal to String : {"type":"submit","form":"#pageForm","data":{"ctl00$MainContentArea$fld_offerCode":"%promo"}},
And i just save in DB this string, this is what I needed. Thanks all.

Categories

Resources