Map String to Object using Jackson with inheritance - java

I have the QueueContent class that it has is a superclass of two others.
I get a String in JSON format that contains the information I need to extract. The super class is:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class QueueContent {
private String empresa;
private String empresa_cor;
private String empresa_contato;
private String empresa_url;
private String empresa_telefone;
private String empresa_idioma;
public QueueContent(String empresa, String empresa_cor, String empresa_contato, String empresa_url, String empresa_telefone, String empresa_idioma) {
this.empresa = empresa;
this.empresa_cor = empresa_cor;
this.empresa_contato = empresa_contato;
this.empresa_url = empresa_url;
this.empresa_telefone = empresa_telefone;
this.empresa_idioma = empresa_idioma;
}
public QueueContent() {
}
}
I'm using Lombok to generate Getters / Setters)
This is the child class:
#Data
public class EmailCameraOffline extends QueueContent {
private Timestamp camera_last_online;
private String camera_nome;
private String empresa_url_plataforma;
public EmailCameraOffline(String empresa, String empresa_cor, String empresa_contato, String empresa_url, String empresa_telefone, String empresa_idioma, Timestamp camera_last_online, String camera_nome, String empresa_url_plataforma) {
super(empresa, empresa_cor, empresa_contato, empresa_url, empresa_telefone, empresa_idioma);
this.camera_last_online = camera_last_online;
this.camera_nome = camera_nome;
this.empresa_url_plataforma = empresa_url_plataforma;
}
public EmailCameraOffline() {
}
}
So I've done:
EmailCameraOffline infosEmail = new ObjectMapper().readValue(content, EmailCameraOffline.class);
System.out.println(infosEmail);
And the output is:
EmailCameraOffline (camera_last_online = 2020-03-12 03: 01: 45.0, camera_nome = Pier Cam 1, empresa_url_platform = null)
How do I get my EmailCameraOffline object to have the superclass attributes initialized?

Everything should be loaded and initialized just fine, so calling:
System.out.println(infosEmail.getEmpresa());
should give expected value.
Problem
The problem is in the default implementation of toString() method (done via #Data) at EmailCameraOffline class, which does not include inherited fields.
Solution
To fix this you can "override" #Data's toString() implementation to include inherited fields as well using Lombok as:
#Data
#ToString(callSuper = true)
public class EmailCameraOffline extends QueueContent {
...
}

Related

MapStruct mapping on objects of type List

I am trying to use MapStruct for a structure similar to the following:
#Data
public class ClassAEntity {
private int id;
private String name;
private String numT;
private List<ClassBEntity) bs;
}
#Data
public class ClassBEntity {
private int id;
private String name;
private String numT;
private List<Other> oc;
}
#Data
public class ClassA {
private int id;
private String name;
private List<ClassB) bs;
}
#Data
public class ClassB {
private int id;
private String name;
private List<Other> oc;
}
In the interface I have added the following mapping:
ClassAEntity map(ClassA classA, String numT)
I get a warning because it can't map numT to classBEntity.numT and I can't add it with #Mapping in the following way:
#Mapping(source = "numT", target = "bs[].numT")
On the other hand I need to ignore the parameter oc of classBEntity because "Other" object contains classAEntity and forms a cyclic object. (because I use oneToMany JPA). I have tried the following:
#Mapping(target = "bs[].oc", ignore = true)
Thank you for your help
MapStruct does not support defining nested mappings for collections. You will have to define more explicit methods.
For example to map numT into bs[].numT and ignore bs[].oc you'll need to do something like:
#Mapper
public MyMapper {
default ClassAEntity map(ClassA classA, String numT) {
return map(classA, numT, numT);
}
ClassAEntity map(ClassA classA, String numT, #Context String numT);
#AfterMapping
default void setNumTOnClassBEntity(#MappingTarget ClassBEntity classB, #Context String numT) {
classB.setNumT(numT);
}
#Mapping(target = "oc", ignore = "true")
ClassBEntity map(ClassB classB);
}

Gson is not ignoring said #Expose annotations [duplicate]

This question already has answers here:
Serialize object using GSON
(2 answers)
Closed 3 years ago.
Code:
public class Crate {
private final MapPosition cratePosition;
private final int tierId;
#Expose(serialize = false, deserialize = false)
private final Inventory inventory;
public Crate(MapPosition cratePosition, int tierId) {
this.cratePosition = cratePosition;
this.tierId = tierId;
this.inventory = Bukkit.createInventory(null, 9*3, "Supply Crate");
}
public void replenishCrates(CrateConfig config) {
List<CrateContent> contents = config.getContentByTier(tierId);
//TODO:
}
public Inventory getInventory() {
return inventory;
}
public Location toLocation(World world) {
return cratePosition.toLocation(world);
}
public MapPosition getCratePosition() {
return cratePosition;
}
public int getTierId() {
return tierId;
}}
The #Expose is being ignored and returning a null pointer exception when trying to deserialize and serialize the class contents. I have made sure to also include the correct GsonBuilder modifications, as stated in Gson's documentation.
The problem you are having is not because #Expose is being ignored but rather because #Expose is missing on the other attributes.
The GsonBuilder's modification you are refering to is the following:
This annotation has no effect unless you build Gson with a GsonBuilder and invoke GsonBuilder.excludeFieldsWithoutExposeAnnotation() method.
But thank God the authors have correctly named the method and it will do just what it is expressing: it will exclude every field that is not marked with the #Expose annotation.
Here is an illustration based on your code (a little bit different because you did not share a completely reproductible sample)
public class Crate {
private final String cratePosition;
private final int tierId;
#Expose(serialize = false, deserialize = false)
private final Inventory inventory;
public Crate(String cratePosition, int tierId) {
this.cratePosition = cratePosition;
this.tierId = tierId;
this.inventory = new Inventory("IV-ID-111000", 10200);
}
public Inventory getInventory() {
return inventory;
}
public int getTierId() {
return tierId;
}
public String getCratePosition() {
return cratePosition;
}
}
And the following test:
public static void main(String[] args) {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Crate crate = new Crate("484:125.52", 1250);
String jsonString = gson.toJson(crate);
System.out.println(jsonString);
String json = "{\"cratePosition\":\"4894:125.52\",\"tierId\":2350}";
Crate deserialized = gson.fromJson(json, Crate.class);
System.out.println(deserialized.getCratePosition() + ":" + deserialized.getTierId());
}
In the current case I have no #Expose annotation on cratePosition and tierId fields, so they are excluded from the serialization and deserialization. Therefore my test returns:
{}
null:0
Now let's add the #Expose annotation on the cratePosition and tierId fields in the Crate class:
#Expose()
private final String cratePosition;
#Expose()
private final int tierId;
By default the serialize and deserialize parameters of the #Expose annotation are both set to true. You can play with it and change the values to see the differences it produces.
If I run the test again I have:
{"cratePosition":"484:125.52","tierId":1250}
4894:125.52:2350

Constructor method must contain all instance variables

Many times I'm faced with a class which constructor method must contain list of arguments that is identical with the list of class instance variables.
As you see in the example there is "SOME" code to make this hapend.
I'm wondering how can I make this process less painful?
Example:
public class VimeoUser extends Schema {
#Getter #Setter private String uri;
#Getter #Setter private String name;
#Getter #Setter private String link;
#Getter #Setter private String location;
#Getter #Setter private String bio;
#Getter #Setter private String createdTime;
#Getter #Setter private String account;
#Getter #Setter private Map<String,Integer> statistics = new HashMap<>();
#Getter #Setter private List<Website> websites = new ArrayList<>();
#Getter #Setter private List<Portrait> portraits = new ArrayList<>();
public VimeoUser(
String uri,
String name,
String link,
String location,
String bio,
String createdTime,
String account,
Map<String,Integer> statistics,
List<Website> websites,
List<Portrait> portraits){
this.uri = uri;
this.name = name;
this.link = link;
this.location = location;
this.bio = bio;
this.createdTime = createdTime;
this.account = account;
this.statistics = statistics;
this.websites = websites;
this.portraits = portraits;
}
}
It is possible to use a pattern named Builder. It is explained in this question
Basically it works as following:
Create an inner static class Builder
Create a private constructor that take as an argument an object of type Builder
In the Builder class add methods that set a single value and returns this (current reference to instance of the Builder class)
In the body of the constructor of your class use the values passed in the Builder to set each property
add a method build in the Builder that calls the private constructor of your class
Here is an example:
public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;
public class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder(int s) {
this.sodium = s;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
and to use it do the following:
NutritionalFacts nutritionalFacts = new NutritionalFacts.Builder()
.fat(200).carbo(50).build();
Using this pattern instead of pojo with setter and getter is useful because it is possible to use it also to build immutable objects (objects with all final fields). An immutable object is useful if you need to share it on a multithreaded environment because it is not necessary to synchronize the access to it.
Additionally it is possible to add some controls in the build method to be sure that all fields are setted as expected.
I guess writing pojos for database modelling does not necessarily needs constructor other than default no-arg constructor. If anyway required in some situations, Getters and setters can be used.
Builder pattern
If you want create a object with more readable way, you can use a simple builder pattern. Lombok support this such as #Getter or #Setter. You just add #Builder annotation and everything should works fine.
#Getter
#Builder
public class SomeClass {
private final String valueOne;
private final String valueTwo;
}
And then you can create object in this way.
SomeClass someClass = SomeClass.builder()
.valueOne("one")
.valueTwo("two")
.build();
Fluent accessors method
Alternative way to create a class is using #Accessors annotation with fluent = true. Then you can create a empty object and set the value what you needed in simple way.
#Getter
#Setter
#Accessors(fluent = true)
public class SomeClass {
private String valueOne;
private String valueTwo;
}
Simple sample using this way.
SomeClass someClass = new SomeClass()
.valueOne("one")
.valueTwo("two");
I see you are already using Lombok. There is a #AllArgsConstructor class-level annotation that will generate the constructor for you. If you want the default constructor, too, use #NoArgsConstructor additionally.
More info on the constructor-generating annotations here.

Design-pattern to use to access similar objects with different field names

What design-pattern, if any, would be most appropriate in this situation.
public class PersonFromDB1 {
private String firstName;
private String lastName;
private String Car;
}
public class PersonFromDB2 {
private String first_name;
private String last_name;
private String boat;
}
Out of these two person types, the only data I would like to work on is fist name and last name regardless of how it field name is name inside the different DBs. firstName and first_name represents the same - name of a person/customer - so does lastName and last-name. The car and boat fields are, in my example, completely irrelevant and should therefore be ignored.
Using, maybe polymorphism or the adapter pattern (?), I would like to create a list of objects that includes persons from DB1 and DB2 under the same type - of PersonInOurDB.
In the end, my goal is to be able to call GSON serialization/desarialization on myClass alone.
public class PersonInOurDB {
private String firstname;
private String lastname;
}
A simple selection based on the type is all you really need. This could be considered a builder pattern because it just initializes a new instance of myClass.
Note, this is rough pseudo code.
FunctionName(SomeType instance)
{
string aPostfix = "_1";
string bPostfix = "_2";
string selectedPostFix;
// This is your strategy selector
switch(typeof(SomeType.Name)
{
case "TypeA":
selectedPostFix = aPostFix;
case "TypeB":
selectedPostFix = bPostFix;
}
return new myClass()
{
A = instance.GetProperty("A" + selectedPostfix).Value,
B = instance.GetProperty("B" + selectedPostfix).Value,
...
}
}
If you want a common access api in java for both objects, then introduce an interface and let both implement it.
If you only want both objects (PersonFromDB1 and PersonFromDB2) to be serialized in the same way by json you can either:
use annotations - the #SerializedName annotation in combination with #Expose.
use the FieldNamingStratgy and ExclusionStrategy
Use annotations to control the serialization
public class PersonFromDB1 {
#Expose
#SerializedName("firstName")
private String firstName;
#Expose
#SerializedName("lastName")
private String lastName;
private String car;
}
public class PersonFromDB2 {
#Expose
#SerializedName("firstName")
private String first_Name;
#Expose
#SerializedName("lastName")
private String last_Name;
private String boat;
}
Then you can use the GsonBuilder
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
PersonFromDB1 person1 = ...; // get the object
PersonFromDB2 person2 = ...; // get the object
System.out.println(gson.toJson(person1));
System.out.println(gson.toJson(person2));
Use FieldNamingStratgy and ExclusionStrategy to control the serialization
If you don't want to modify the db objects (you can't or you don't want to add annotations) than there is another way. You can use a FieldNamingStratgy and ExclusionStrategy.
class PersonFromDBNamingStrategy implements FieldNamingStrategy {
Map<String, String> fieldMapping = new HashMap<String, String>();
public PersonFromDBNamingStrategy() {
fieldMapping.put("first_Name", "firstName");
fieldMapping.put("last_Name", "lastName");
}
#Override
public String translateName(Field f) {
String name = f.getName();
if(fieldMapping.contains(name)){
return fieldMapping.get(name);
}
return name;
}
}
and the ExclusionStrategy
class PersonFromDExclusionStrategy implements ExclusionStrategy {
List<String> validNames = Arrays.asList("car", "boat");
#Override
public boolean shouldSkipField(FieldAttributes f) {
String name = f.getName();
return !validNames.contains(name);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
after that just create Gson like this:
GsonBuilder gsonBuilder = new GsonBuilder();
sonBuilder.addSerializationExclusionStrategy(new PersonFromDExclusionStrategy());
gsonBuilder.setFieldNamingStrategy(new PersonFromDBNamingStrategy());
Gson gson = gsonBuilder.create();
PersonFromDB1 person1 = ...; // get the object
PersonFromDB2 person2 = ...; // get the object
System.out.println(gson.toJson(person1));
System.out.println(gson.toJson(person2));

Jackson JSON field mapping capitalization?

I'm not clear how jackson deals with capitalization in mapping fields. If anyone could help I'd appreciate it.
{"user":{"username":"user#host.com","password":"pwd","sendercompid":"COMPID","service":{"host":"address","port":6666,"service":"S1","serviceAsString":"s1"}},"MDReqID":"ghost30022","NoRelatedSym":1,"Symbol":["GOOG"],"MarketDepth":"0","NoMDEntryTypes":3,"MDEntryType":["0","1","2"],"SubscriptionRequestType":"1","AggregatedBook":"N"}:
Above is my json, below is my exception...
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "MDReqID" (class com.myco.qa.fixrest.MarketDataRequest), not marked as ignorable (10 known properties: , "mdreqID", "marketDepth", "user", "subscriptionRequestType", "aggregatedBook", "mdentryType", "symbol", "mdupdateType", "noRelatedSym", "noMDEntryTypes"])
Above is my exception, below is my class...
public class MarketDataRequest {
private User user;
private String MDReqID;
private char SubscriptionRequestType;
private int MarketDepth;
private int MDUpdateType;
private char AggregatedBook;
private int NoMDEntryTypes;
private ArrayList<Character> MDEntryType;
private int NoRelatedSym;
private ArrayList<String> Symbol;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getMDReqID() {
return MDReqID;
}
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
public char getSubscriptionRequestType() {
return SubscriptionRequestType;
}
public void setSubscriptionRequestType(char subscriptionRequestType) {
SubscriptionRequestType = subscriptionRequestType;
}
... et cetera
Since your setter method is named setMDReqID(…) Jackson assumes the variable is named mDReqID because of the Java naming conventions (variables should start with lower case letters).
If you really want a capital letter use the #JsonProperty annotation on the setter (or - for serialization - on the getter) like this:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
You can also do
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
on the class to capitalise all property names in the JSON message
Add #JsonProperty on the setter that matches the property name in your received JSON string:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
Additionally add #JsonProperty annotation to the getter as well for your output to appear in the conventional format:
#JsonProperty("mDReqID")
public String getMDReqID() {
return MDReqID;
}
Now you can name your variable whatever you like:
private String mdReqID;
I solve this problem by:
#Getter
#Setter
static class UserInfo {
//#JsonProperty("UUID")
private String UUID = "11";
private String UserName = "22";
private String userName = "33";
private String user_Name = "44";
private String user_name = "55";
private String User_name = "66";
private boolean HasDeleted=true;
private boolean hasDeleted=true;
private boolean has_Deleted=true;
private boolean has_deleted=true;
private boolean HAS_DELETED=true;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String s = objectMapper.writeValueAsString(new UserInfo());
System.out.println(s);
UserInfo userInfo = objectMapper.readValue(s, UserInfo.class);
System.out.println(objectMapper.writeValueAsString(userInfo));
}
output:
{"UUID":"11","UserName":"22","userName":"33","user_Name":"44","user_name":"55","User_name":"66","HasDeleted":true,"hasDeleted":true,"has_Deleted":true,"has_deleted":true,"HAS_DELETED":true}
I face the same problem , after have try UpperCamelCaseStrategy but still this error occurred , the strategy made my field pContent to ObjectMapper property Pcontent, as not want to add #JsonProperty for every field, simply use gson instead at last
Use JsonNaming Annotation to get all Class Field Names in Proper Case
Use lombok.Data Annotation to automatically make it work without adding getters and setters in your class
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import lombok.Data;
#JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
#Data

Categories

Resources