Mapping part of a JSON string to an abstract class using GSON - java

I have a json string that I would like to map to my java object. I am currently using gson to do so. The problem is however, I have setup part of my POJO to contain an abstract class. How can I map the json that corresponds to this abstract class correctly?
To clarify:
Here is an example of a json string I am currently receiving:
{
"Items" : [
{
"id" : "ID1",
"seller_id": 17,
"item_plan": {
"action" : "Sell"
}
},
{
"id" : "ID2",
"seller_id": 27,
"item_plan": {
"action": "Remove",
}
}
]
}
My request object is setup like so:
public class RequestObject {
#SerializedName("Items")
#Expose
private List<Item> items = null;
public class Item {
#SerializedName("id")
#Expose
private String id;
#SerializedName("seller_id")
#Expose
private Integer sellerID;
#SerializedName("item_Plan")
#Expose
private ItemPlan item_plan;
public abstract class ItemPlan {
#SerializedName("action")
#Expose
private String action;
public abstract void executePlan()
As you can see, my request object has an abstract class that represents item_plan. The idea here is that item_plan actions will have their own way of execution and therefore have a parent class called ItemPlan where each child class would represent the possible action plans and their own executionPlan ie. (SellPlan is a child class of ItemPlan, where SellPlan has its own implementation of the function executionPlan()).
How can I map my example json string to the following Java classes?
I have tried the following:
RuntimeTypeAdapterFactory<ItemPlan> itemPlanRuntimeTypeAdapterFactory =
RuntimeTypeAdapterFactory
.of(ItemPlan.class, "action")
.registerSubtype(SellPlan.class, "Sell")
.registerSubtype(RemovePlan.class, "Remove");
Gson gson = new
GsonBuilder().registerTypeAdapterFactory(itemPlanRuntimeTypeAdapterFactory).create();
RequestObject request = gson.fromJson(jsonString, RequestObject.class);
This, however, does not work. It is able to map everything I need but it fails to create the correctly create the abstracted class objects ie. while it will create the corresponding child objects (SellPlan for Sell and RemovePlan for Remove), it will make the action string of those classes null. There is a workaround where I can simply set the action string manually in the constructor of these classes but I would rather not. Is there a way to fix this?
Thank you.

You probably have to use the RuntimeTypeAdapterFactory.of overload with the additional maintainType parameter for which you then pass true as value. Otherwise, as you have noticed, Gson removes the type field value during serialization, and therefore the field keeps its default value null.

Related

Spring Webflux - WebClient: How to get a List<TheNestedPojo> nested inside a larger Response directly?

Small question regarding Spring Webflux, and how to get the nested List of Pojo that is present in a http response directly.
We are consuming an API which response is something like
{
"noNeedThisField": "I do not need this",
"listOfWhatIwant": [
{
"personName": "Alice",
"personAge": "11"
},
{
"personName": "Bob",
"personAge": "22"
},
{
"personName": "Charlie",
"personAge": "33"
}
],
"uselessField": "This is useless",
"manyFieldsNoNeed": "it is one response, which contains a lot of fields that I do not need, I just need to retrieve the list DIRECTLY please",
"noNeed": true,
"anotherNotImportant": "this is not important at all"
}
Basically, it is one response, which contains a lot of fields I do not need, plus an element of type list in it, which I would like to get directly.
If I create two different classes, first one
public class PojoWithListAndOtherNoNeedFields {
private String noNeedThisField;
private List<MyNestedPojo> listOfWhatIwant;
private String uselessField;
private String manyFieldsNoNeed;
private boolean noNeed;
private String anotherNotImportant;
}
//getters setters
second one
public class MyNestedPojo {
private String personName;
private String personAge;
//getters setters
}
And invokes Webclient like this:
public Mono<PojoWithListAndOtherNoNeedFields> sendReqest() {
return webClient.mutate().baseUrl("url").build().post().uri("/route").retrieve().bodyToMono(PojoWithListAndOtherNoNeedFields.class);
}
It is working fine! I just need to carry a very large class that I do not need in my code, and retrieve the nested list of what I need with a getter each time.
However, I was wondering is it is possible to do something similar as (this is not working)
public Mono<List<MyNestedPojo>> sendReqest() {
return webClient.mutate().baseUrl("url").build().post().uri("/route").retrieve().bodyToMono(List<MyNestedPojo>.class);
}
In order to retrieve the nested element directly.
My goal is to get rid of PojoWithListAndOtherNoNeedFields entirely, and getting the List< MyNestedPojo> directly. Is it possible?
How to perform this in a proper way in Spring using the Webclient please?
Thank you
You can use the #JsonIgnoreProperties annotation to inform the ObjectMapper to ignore any fields not included in your POJO when deserialisating from json to a POJO.
#JsonIgnoreProperties(ignoreUnknown = true)
public class PojoWithListAndOtherNoNeedFields {
private List<MyNestedPojo> listOfWhatIwant;
}
public class MyNestedPojo {
private String personName;
private String personAge;
}
JavaDocs

Object mapper removing empty and null values when converting object to string Java

In response to api call, i'm sending Json Class Object as response.
I need response like this without empty objects being removed.
{
"links": {
"products": [],
"packages": []
},
"embedded":{
"products": [],
"packages": []
}
}
but final Response is looking like this
{
"links": {},
"embedded": {}
}
Two things to be aware of:
null and empty are different things.
AFAIK Jackson is configured to serialize properties with null values by default.
Make sure to properly initialize your properties in your object. For example:
class Dto {
private Link link;
private Embedded embedded;
//constructor, getters and setters...
}
class Link {
//by default these will be empty instead of null
private List<Product> products = new ArrayList<>();
private List<Package> packages = new ArrayList<>();
//constructor, getters and setters...
}
Make sure your classes are not extending another class with this annotation #JsonInclude(JsonInclude.Include.NON_NULL). Example:
//It tells Jackson to exclude any property with null values from being serialized
#JsonInclude(JsonInclude.Include.NON_NULL)
class BaseClass {
}
//Any property with null value will follow the rules stated in BaseClass
class Dto extends BaseClass {
private Link link;
private Embedded embedded;
//constructor, getters and setters...
}
class Link extends BaseClass {
/* rest of the design */
}
If you have the latter and you cannot edit BaseClass then you can define different rules in the specific classes:
class Link extends BaseClass{
//no matter what rules are defined elsewhere, this field will be serialized
#JsonInclude(JsonInclude.Include.ALWAYS)
private List<Product> products;
//same here
#JsonInclude(JsonInclude.Include.ALWAYS)
private List<Package> packages;
//constructor, getters and setters...
}

Java: How to structure or pattern a class so that we can map composed Object during Runtime?

Consuming a rest api which is returning json which has some fixed format and other change per endpoint. I am using gson library which maps incoming json to java classes.
So I have following abstract Resource:
#Getter
#Setter
public class AbstractResource {
#SerializedName("Meta")
#Expose
private List<Meta> meta = null;
#SerializedName("Body")
#Expose
private List<AbstractBody> body = null;
AbstractBody has following contents:
public class AbstractBody {
#SerializedName("Body")
#Expose
private Computer computer = null;
#SerializedName("Links")
#Expose
private List<Link> links = null;
AbstractResource is common for all endpoints but In Abstract body, It returns Computer in one endpoint and in other endpoint it returns Licenses and other Endpoint returns Clusters. All comes in Body field of json.So everytime AbstractBody gets changes.
Currently i have to make both classes again and again in different packages. So in main things goes like.
ComputerResource agreement = gson.fromJson(json, ComputerResource .class);
I want to make common package for both abstractresoource and abstractbody and at run time it should decide which class it should get into.
How can i modify above structure to do that?
You could use TypeToken:
Represents a generic type T. Subclassing TypeToken allows for type information to be preserved at runtime.
Let's assume we have two JSON structures:
{"meta": "value", "body": {"license": "true"}}
{"meta": "value", "body": {"computer": "true"}}
They are represented by a generic class AbstractResource
public class AbstractResource<T> {
private String meta;
private T body;
}
public class Computer {
private boolean computer;
}
public class License {
private boolean license;
}
When deserializing, Gson needs to be told how to deal with the generic body which can be supplied via TypeToken:
Type computer = new TypeToken<AbstractResource<Computer>>() {}.getType();
AbstractResource<Computer> resource = g.fromJson(json, computer);
Type license = new TypeToken<AbstractResource<License>>() {}.getType();
AbstractResource<License> resource = g.fromJson(json, license);
This allows for deserializing arbitrary nested structures into a generic field body of type T. It can be easily adapted to handling a list of nested structures via changing bodys type to List<T>.

Spring Android client : get request returning more complexe JSON objects

I'm trying to include Spring for Android as a client to a Spring server in a mobile app project. After looking at the documentation (https://docs.spring.io/spring-android/docs/1.0.1.RELEASE/reference/html/rest-template.html) I would like to use
the method getForObject() to directly create the objects I will use in the code. All the exemples I find on internet only show what to do with more primitive type for the value of each key (String, long, int). The JSON object sent back from the server looks like this :
{
"id": "MSP-SS-043208",
"nom": "Inondation-Débit-Niveau",
"source": "Ministère de la Sécurité publique du Québec",
"territoire": "Rivière des Mille Îles(043208)",
"certitude": "Observé",
"severite": "Mineure",
"dateDeMiseAJour": "lundi 04 juin 2018",
"urgence": "Future",
"description": "Seuil de surveillance atteint",
"count": 1,
"geometry": {
"type": "Point",
"coordinates": [-73.6387202781213, 45.6928705203507]
},
"type": "Suivi des cours d'eau"
}
My problem is I have no idea what to do with the geometry key because it is not a primitive object ! How can I make Spring recognize that there is a JSON object as the attribute of a certain key ? And what about the array of double (coordinates)?
This is the class I am trying to use:
public class Alert {
private String id;
private String nom;
private String source;
private String territoire;
private String certitude;
private String severite;
private String dateDeMiseAJour;
private String urgence;
private String description;
private int count;
private ????? geometry;
private String type;
... gettters and setters ...
}
My question comes down to : How must I declare the geometry attribute to make sure the objects are created correctly ?
You need to make a Geometry class that has some deserialization logic, and then declare private Geometry geometry as a member of the Alert class.
This deserialization is usually performed "automagically" by Spring (specifically, by the fasterxml library provided by jackson). Since the Geometry class looks like it has some nontrivial objects, you'll also need to define a Coordinates class and 'Type' class (or Enum) that can similarly be deserialized.
Sometimes this approach can create a lot of small classes that take a lot of work to deal with. Another approach is to use the #JsonCreator annotation on a constructor of the Alert class:
#JsonCreator
public Alert(Map<String, Object> params) {...}
Then you can parse the Alert json manually.

Serialization pojo into different json structure

I need to serialize a pojo into different json structure depending on whom I am sending request. Also I should be able to configure in some config that how field of pojo are mapped to json properties for a given request.
Can this be achived using jackson?
Is there some library or api to do this?
Edit:
For example:
public class Universal {
private int id;
private Date date;
private String name;
private Inner inner;
private Map<String,Object> others;
private List<Inner> inners;
}
public class Inner {
private String value;
}
now above are two object i need to create dynamic json, one example for some of transformation is below
{
"id":"",//value will be id of Universal
"detials":{
"name":"",//value will be name of Universal
},
"data":[], // array of value(field of Inner) from inners
"ext":{
"prop1":""// value of this field will be some (key1) value from others
}
}
You can use Google Gson and rely on its type adaptors.
http://www.javacreed.com/gson-typeadapter-example/ is a good article from web

Categories

Resources