How can I convert enum to POJO using mapstruct and without custom implementation?
e.g.
enum Type {
T1, T2;
private String description;
private Type(String description) {
this.description = description;
}
public String getDescription() { return this.description; }
}
to POJO like
class TypeDto {
private Type code;
private String description;
}
FYI,
I use MapStruct 1.1.0.Final.
I use this for now
default TypeDto typeToTypeDto(Type type) {
return new TypeDto(type.name(), type.getName());
}
due to lack of another solution.
You cannot directly convert from an enum to an object.
You would need to create a TypeMapper and an implementation to handle the conversion.
TypeConversion
public class TypeConversion {
public static void main(String[] args) {
TypeDto t1 = TypeMapper.INSTANCE.typeToTypeDto(Type.T1);
TypeDto t2 = TypeMapper.INSTANCE.typeToTypeDto(Type.T2);
System.out.println(t1);
System.out.println(t2);
}
}
Type
public enum Type {
T1("T-One"),
T2("T-Two");
private final String description;
private Type(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
}
TypeDto
public class TypeDto {
private String description;
public TypeDto() {
this("");
}
public TypeDto(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return String.format("TypeDto { \"description\": \"%s\" }", description);
}
}
TypeMapper
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
#Mapper
public interface TypeMapper {
TypeMapper INSTANCE = Mappers.getMapper(TypeMapper.class);
#Mapping(source = "description", target = "description")
TypeDto typeToTypeDto(Type type);
}
TypeMapperImpl
public class TypeMapperImpl implements TypeMapper {
#Override
public TypeDto typeToTypeDto(Type type) {
if (type == null) {
return null;
}
return new TypeDto(type.getDescription());
}
}
You can make this mapper reusable by creating a generic mapper.
EnumMapper
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
#Mapper
public interface EnumMapper<T, U extends Enum<?>> {
#Mapping(target = "description")
T enumToObject(U type);
}
EnumMapperImpl
public abstract class EnumMapperImpl<T, U extends Enum<?>> implements EnumMapper<T, U> {
#Override
public T enumToObject(U type) {
if (type == null) {
return null;
}
return convert(type);
}
protected abstract T convert(U type);
}
Then you can use this in your TypeMapper.
TypeMapper
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
#Mapper
public interface TypeMapper extends EnumMapper<TypeDto, Type> {
TypeMapper INSTANCE = Mappers.getMapper(TypeMapper.class);
}
TypeMapperImpl
public class TypeMapperImpl extends EnumMapperImpl<TypeDto, Type> implements TypeMapper {
#Override
protected TypeDto convert(Type type) {
return new TypeDto(type.getDescription());
}
}
It's not something that MapStruct can automatically handle for you. Just implement the mapping by hand. MapStruct does not aim to handle every mapping case for you, but automate the common 80% and let you deal with the more exotic cases yourself.
Related
We would like to type various properties in Java.
e.g. the e-mail address
But now I get the message all the time:
Could not set field value [test#test.de] value by reflection : [class customer.email] setter of customer.email;
Can not set dataType.EmailAddress field customer.email to java.lang.String
How should I proceed?
#Entity
public class customer {
#Id
#GeneratedValue
private Long id;
private String name;
private EmailAddress email;
}
public class EmailAddress {
public String value;
public EmailAddress(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public boolean setValue(String s) {
this.value = s;
return true;
}
public String mailbox() ...
public String host() ...
public String tld() ...
}
Getter and Setter from HibernateDefaultType not called.
EDIT:
At the end. I want to store a String in the database with the email-Address. In Java I want the EmailAddress Object.
it is much easier. An AttributeConverter make it very easy.
https://thorben-janssen.com/jpa-attribute-converter/
Thank you very much
EDIT:
Here is the Code:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class EmailAddressConverter implements AttributeConverter<EmailAddress, String> {
#Override
public String convertToDatabaseColumn(EmailAddress emailAddress) {
return emailAddress.value;
}
#Override
public EmailAddress convertToEntityAttribute(String s) {
return new EmailAddress(s);
}
}
And in the Entity:
#Convert(converter = EmailAddressConverter.class)
private EmailAddress email;
Here is some example of making your own custom type.
public class EmailAddressDescriptor extends AbstractTypeDescriptor<String> {
protected EmailAddressDescriptor() {
super(String.class, new ImmutableMutabilityPlan<>());
}
#Override
public String toString(String value) {
return null;
}
#Override
public String fromString(String string) {
return null;
}
#Override
public <X> X unwrap(String value, Class<X> type, WrapperOptions options) {
return null;
}
#Override
public <X> String wrap(X value, WrapperOptions options) {
return null;
}
#Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
return null;
}
}
Then you would make the Email address class with all your methods
public class EmailAddress extends AbstractSingleColumnStandardBasicType<String> {
private String value;
public EmailAddress() {
super(new VarcharTypeDescriptor(), new EmailAddressDescriptor());
}
#Override
public String getName() {
return "EmailAddress";
}
#Override
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) throws HibernateException {
return null;
}
}
public String mailbox() ...
public String host() ...
public String tld() ...
How you would use it with your entity will be something like this
#Entity
#TypeDef(name = "emailAddress", typeClass = EmailAddress.class)
public class customer {
#Id
#GeneratedValue
private Long id;
private String name;
#Type (type = "emailAddress")
private EmailAddress emailAddress;
}
Hope this helps
When defining an abstract class, it is possible to create an instance of that class by writing the body of the abstract methods at object definition, like this:
AbstractClass obj = new AbstractClass() {
protected String description() { return this.description; }
};
I would like to do something similar, but inside the constructor of a sub-class. Something like this:
public class AbstractClass {
String description;
public AbstractClass(String description){
this.description = description;
}
protected abstract String description();
}
public class ActualClass extends AbstractClass {
public ActualClass(String description){
super(description) {
protected String description() { return this.description; }
};
}
}
Now, the code above doesn't work. How could I do something similar?
You don't do it in constructor, but in the class itself:
public abstract class AbstractClass {
String description;
public AbstractClass(String description){
this.description = description;
}
protected abstract String description();
}
public class ActualClass extends AbstractClass {
public ActualClass(String description){
super(description);
}
protected String description() {
return this.description;
}
}
For the below code, my nested parameterized object is always deserialized as LinkedTreeMap instead of the original class
I am using GSON for json serializing & deserializing
Here are the models:
Cart containing wrapped items
public class Cart {
private String id;
private String owner;
private List<ItemWrapper> relatedItems;
.......
public List<ItemWrapper> getRelatedItems() {
return relatedItems;
}
public void setRelatedItems(List<ItemWrapper> relatedItems) {
this.relatedItems = relatedItems;
}
}
Item wrapper
public class ItemWrapper<T> {
private String type;
private String decription;
private T item;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
........
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Car Item ..
public class Car {
private String model;
private double price;
public String getModel() {
return model;
}
.....
public void setPrice(double price) {
this.price = price;
}
}
Book Item ..
public class Book {
private String name;
private String mediaType;
public String getName() {
return name;
}
.....
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
}
When I run the below snippet
Cart cart = gson.fromJson(
"{\"id\":\"id123\",\"owner\":\"Usama\",\"relatedItems\":[{\"type\":\"Book\",\"decription\":\"book item\",\"item\":{\"name\":\"Love\",\"mediaType\":\"pdf\"}},{\"type\":\"Car\",\"decription\":\"car item\",\"item\":{\"model\":\"BMW\",\"price\":500000.0}}]}\n"
+ "",
Cart.class);
System.out.println(cart.getClass().getName());
System.out.println(cart.getRelatedItems().get(0).getItem().getClass().getName());
I got that result
model.Cart
com.google.gson.internal.LinkedTreeMap
instead of
model.Cart
model.Book
Any idea how to fix this.
I have generic abstract class:
#MappedSuperclass
public abstract class Generic<T extends Generic> {
#Transient
public Class<T> entityClass;
Generic() {
entityClass = ((Class) ((Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]));
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
#ManyToOne(cascade = CascadeType.MERGE)
#Constraints.Required
public User creationUser;
#Constraints.Required
public String creationDate = Index.getDate(null);
#Constraints.Required
public String updateDate = Index.getDate(null);
public User getCreationUser() {return creationUser;}
public void setCreationUser(User user) {this.creationUser = user;}
public void setCreationUser() {this.creationUser = User.getCurrentUser();}
public String getCreationDate() {return creationDate;}
public void setCreationDate(String creationDate) {this.creationDate = creationDate;}
public String getUpdateDate() {return updateDate;}
public void setUpdateDate(String updateDate) {this.updateDate = updateDate;}
public T getBy(Long id) {
return JPA.em().find(entityClass, id);
}
public List<T> getList() {
Logger.debug("TEST");
return (List<T>) JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
}
public List<T> getByUser_id(Long id) {
List<T> entities = new ArrayList<T>();
TypedQuery<T> query = JPA.em().createQuery("SELECT r FROM " + entityClass.getSimpleName() + " r WHERE r.user.id != :user_id", entityClass).setParameter("user_id", id);
try {
entities = query.getResultList();
} catch (NoResultException e) {
entities = null;
}
return entities;
}
#PrePersist
public void prePersist() {
Logger.warn(this.toString());
setCreationDate(Index.getDate(null));
preUpdate();
}
#PreUpdate
public void preUpdate() {
setUpdateDate(Index.getDate(null));
}
public void toDataBase() {
JPA.em().persist(this);
}
public void update() {
JPA.em().merge(this);
}
/**
* A Generic toString method that can be used in any class.
* uses reflection to dynamically print java class field
* values one line at a time.
* requires the Apache Commons ToStringBuilder class.
*/
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
Which have getList method
This class is extendet twice
Firt extend class is:
#MappedSuperclass
public abstract class GenericDictionary<T extends Generic> extends Generic<GenericDictionary> {
#Required
public String name;
#Required
public boolean active;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getActive() {
return active;
}
public void setActive(boolean stat) {
this.active = stat;
}
/**
* Options.
*
* #return the map
*/
public Map<String, String> getOptions() {
LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
for (GenericDictionary e : getList()) {
options.put(e.id.toString(), e.name);
}
return options;
}
public void on() {
this.active = true;
update();
}
public void off() {
this.active = false;
update();
}
}
Which dosnt contain getList method
And finally second extend class is:
#Entity
#Table(name="common__Clinic")
public class Clinic extends GenericDictionary<Clinic> {
#ManyToMany(cascade = CascadeType.PERSIST)
public List<Unit> unit;
}
Which also dosnt contain getList method
Know I would like to download all my Clinics using inheritated method: getList from Generic class.
I use that code for this:
new Clinic().getList()
But unfortunatelly I am getting:
List<GenericDictionary>
instead of
List<Clinic>
How can I download List of clinic using inheritated method?
Change
public abstract class GenericDictionary<T extends Generic> extends Generic<GenericDictionary> {
to
public abstract class GenericDictionary<T extends Generic> extends Generic<T> {
As it stands, Clinic extends Generic<GenericDictionary> - not Generic<Clinic>.
Working solution:
In controller, I've changed method from static:
Clinic.options()
To dynamic:
new Clinic().getList()
In model, I've changed method getOptions. In the body I've added casting From getList() (which returns Generic), to GenericDictionary. Here is whole genericDictionary model.
package models;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.MappedSuperclass;
import play.Logger;
import play.data.validation.Constraints;
import play.data.validation.Constraints.Required;
import play.db.jpa.JPA;
#MappedSuperclass
public abstract class GenericDictionary<T extends Generic<T>> extends Generic<T> {
#Required
public String name;
#Required
public boolean active;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getActive() {
return active;
}
public void setActive(boolean stat) {
this.active = stat;
}
/**
* Options.
*
* #return the map
*/
public Map<String, String> getOptions() {
LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
for (T e : getList()) {
GenericDictionary entity = (GenericDictionary) e;
options.put(e.id.toString(), entity.name);
}
return options;
}
// #Override
// public List<GenericDictionary> getList() {
// return (List<GenericDictionary>) JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
// }
public void on() {
this.active = true;
update();
}
public void off() {
this.active = false;
update();
}
}
I have following json data
{"id":10606,
"name":"ProgrammerTitle",
"objectMap":{"programme-title":"TestProgramme","working-title":"TestProgramme"}
}
I want to set this data to my pojo object
public class TestObject {
private Long id;
private String name;
#JsonProperty("programme-title")
private String programmeTitle;
#JsonProperty("working-title")
private String workingTitle;
}
Here i am able to set id and name in my test object but for object map i am not able to set data.
So i have made on more class for ObjectMap which contains programmeTitle & workingTitle this works fine but i can't set this fields directly to my pojo object
is this possible to set?
I am using Jackson Object Mapper to convert json data.
It is working fine if i create another java object inside my pojo like:
public class TestObject {
private Long id;
private String name;
#JsonProperty("objectMap")
private ObjectMap objectMap;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ObjectMap getObjectMap() {
return objectMap;
}
public void setObjectMap(ObjectMap objectMap) {
this.objectMap = objectMap;
}
}
public class ObjectMap {
#JsonProperty("programme-title")
private String programmeTitle;
#JsonProperty("working-title")
private String workingTitle;
public String getProgrammeTitle() {
return programmeTitle;
}
public void setProgrammeTitle(String programmeTitle) {
this.programmeTitle = programmeTitle;
}
public String getWorkingTitle() {
return workingTitle;
}
public void setWorkingTitle(String workingTitle) {
this.workingTitle = workingTitle;
}
}
If your JSON is like this
{"id":10606,
"name":"ProgrammerTitle",
"objectMap":{"programme-title":"TestProgramme","working-title":"TestProgramme"}
}
then you may write your object mapper class like this..
public class Program{
public static class ObjectMap{
private String programme_title, working_title;
public String getprogramme_title() { return programme_title; }
public String getworking_title() { return working_title; }
public void setprogramme_title(String s) { programme_title= s; }
public void setworking_title(String s) { working_title= s; }
}
private ObjectMap objMap;
private String name;
public ObjectMap getobjectMap () { return objMap; }
public void setObjectMap (ObjectMap n) { objMap= n; }
private Long id;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
please refer this check it
You can write your own deserializer for this class:
class EntityJsonDeserializer extends JsonDeserializer<Entity> {
#Override
public Entity deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Root root = jp.readValueAs(Root.class);
Entity entity = new Entity();
entity.setId(root.id);
entity.setName(root.name);
if (root.objectMap != null) {
entity.setProgrammeTitle(root.objectMap.programmeTitle);
entity.setWorkingTitle(root.objectMap.workingTitle);
}
return entity;
}
private static class Root {
public Long id;
public String name;
public Title objectMap;
}
private static class Title {
#JsonProperty("programme-title")
public String programmeTitle;
#JsonProperty("working-title")
public String workingTitle;
}
}
Your entity:
#JsonDeserialize(using = EntityJsonDeserializer.class)
class Entity {
private Long id;
private String name;
private String programmeTitle;
private String workingTitle;
//getters, setters, toString
}
And usage example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Entity entity = mapper.readValue(jsonString, Entity.class);
System.out.println(entity);
}
}
Above program prints:
Entity [id=10606, name=ProgrammerTitle, programmeTitle=TestProgramme, workingTitle=TestProgramme]