I tried googling the below with almost no luck. I have a very different scenario from what I got in search results.
I have a json object which has a key(key1) with the following structure
"key1": {
"key2": "some value1",
"key3": {
"key4": "some value2",
"key5": "some value3"
}
}
key 3 is dynamic in that it can have the following data as well
"key1": {
"key2": "some value1",
"key3": {
"key4": "some value2",
"key5": "some value3"
"key6": "some value4"
}
}
or
"key1": {
"key2": "some value1",
"key3": {
"key4": "some value2",
}
}
only these 3 combinations are possible(key 3 having 2 attributes, 3 attributes or just 1 atrribute)
with all that in mind, I declared an abstract class(let's say Class Key3) for key3 in java thinking that key3 can have multiple implementations(Each concrete class wil have their own fields).
Now there is a POJO composed of class Key3's variable(having a reference variable of Key3)
public Class MYPOJO class{
String variable1;
String variable2;
.
.
.
Key3 variable;
.
.
.
int variable3;
}
Now as the deserialization happens for MYPOJO, it fails as Key3 is abstract and hence can't be instantiated with a desired concrete class(concrete class will be either of the three classes - having 1, 2 or 3 fields). The concrete class can be decided on the basis of the value of key2 or whatever is present in key3 in json.
Can ayone suggest me something that can take care of this - instantiate a concrete class and pass it on to the reference variable of abstract class?
Or some better workaround to deal with it?
Any suggestions would be appreciated.
Thanks in advance
CodePudding user response:
I don't know in what environment you want to use this deserialization method, so let me tell you how to do it in springboot. If it is key1, key2, key3 is determined, I can use the following methods to receive.
@PostMapping("/test")
public String test(@RequestBody Test userName){
System.out.println(userName);
return null;
}
@Data
@ToString
@Accessors(chain = true)public class Test {
private String key;
private String key2;
private Map<String, Object> key3;
}
The console prints the result as Test(key=aaa, key2=some value1, key3={key4=16, key5=jsf}).
It is proved that the deserialization can be completed by using map to receive multiple parameters, however, this method requires key1, key2, and key3 to be fixed names.
CodePudding user response:
So basically you have two options. If you know there'll always be the same structure you can create classes for response and deserialize with for example GSON. There are always three fields in object and the ones that are not in JSON response are null. The second option is to create list of strings in Key3 class to store those values but it has to be done manually.
Case 1:
public class Response {
@SerializedName("key1")
private Key1 key1;
// getters, setters
}
Key1 class
public class Key1{
@SerializedName("key2")
private String key2;
@SerializedName("key3")
private Key3 key3;
// getters, setters
}
Key3 class
public class Key3{
@SerializedName("key5")
private String key5;
@SerializedName("key6")
private String key6;
@SerializedName("key4")
private String key4;
// getters, setters
}
Then you can deserialize JSON response with following code
GsonBuilder gsonBuilder = new GsonBuilder();
Response response = gsonBuilder.create().fromJson(jsonResponse, Response.class);
System.out.println(response);
Keep in mind if JSON response doesnt contain, for example key6, this value will be null.
Case 2:
public class Key3{
List<String> keys = new ArrayList<>();
// geters, setters
}
Deserialization
Gson gson = new Gson();
Object o = gson.fromJson(jsonResponse, Object.class);
if (o instanceof Map) {
Map map = (Map) o;
Collection firstLevelBranchMapValues = map.values();
Key1 key1 = new Key1();
for (var val : firstLevelBranchMapValues) {
if (val instanceof LinkedTreeMap){
Map secondLevelBranchMap = ((LinkedTreeMap) val);
for (var secondLevelBranchMapValues : secondLevelBranchMap.values()) {
if (secondLevelBranchMapValues instanceof LinkedTreeMap){
// map of some value2, some value3, some value4
Map thirdLevelBranchMap = ((LinkedTreeMap) secondLevelBranchMapValues);
Key3 key3 = new Key3();
for (var thirdLevelBranchMapValues : thirdLevelBranchMap.values()){
// returns some value2, some value3, some value4 in following iterations
key3.getKeys().add(thirdLevelBranchMapValues.toString());
}
key1.setKey3(key3);
} else {
// returns some value1
key1.setKey2(secondLevelBranchMapValues.toString());
}
}
}
}
}