I have the following class structure (it actually is a VO layer with Hibernate mappings):
public abstract class abstractClassVO {
private int id;
private String name;
}
public class concreteClassAVO extends abstractClassVO {
private String aAttribute;
}
public class concreteClassBVO extends abstractClassVO {
private Long bAttribute;
}
And the equivalent DTO objects:
public abstract class abstractClassDTO {
private int id;
private String name;
}
public class concreteClassADTO extends abstractClassDTO {
private String aAttribute;
}
public class concreteClassBDTO extends abstractClassDTO {
private Long bAttribute;
}
Then I have another object like this:
public class compositeObject {
private int anAttribute;
private abstractClassVO myInstance;
}
and its equivalent:
public class compositeObjectDTO{
private int anAttribute;
private abstractClassDTO myInstance;
}
How can I tell dozer to automatically map myInstance to the specific DTO that corresponds to the concrete class implementation in the VO layer?
Currently, out of the box, Dozer isn't even putting anything in the myInstance field of the compositeObjectDTO class. My guess is that it's due to the fact that abstractClassDTO it is an abstact class, and since it cannot determine the implementation, it does nothing. I am not getting any exceptions.
Dozer can't do it out of the box but you could write a helper that would determine destination class by source class. You can get this information from DozerBeanMapper.getMappingMetadata().getClassMappings* methods. These methods return list of ClassMappingMetadata that contains destination class. You just only need to chech whether destination class is inherited from abstractClassDTO. This check can be omitted if you only have one mapping for one VO.
For bi-directional mapping you should additionally check ClassMappingMetadata.MappingDirection field.
Related
I have the following classes Entity, BaseClass, SubClass1, SubClass2.
public class Entity {
private String field1;
private String field2;
private String type;
private String field3;
private String field4;
}
public class BaseClass {
private String field1;
private String field2;
private String type;
}
public class SubClass1 extends Base {
private String field3;
}
public class SubClass2 extends Base {
private String field4;
}
Entity is obtained from by call an API. Depending on the value of the type property of the BaseClass, I want to map the Entity to the corresponding subclass.
One option I can think of mapping is below,
#Mapper
public interface EntityMapper {
SubClass1 mapEntityToSubClass1(Entity entity);
SubClass2 mapEntityToSubClass2(Entity entity);
}
I will invoke either of the methods mapEntityToSubClass1 or mapEntityToSubClass2 conditionally based on the type in my business logic. Since I will be getting a List from the external API, looping through, checking the type in Entity doesn't seem good to me. In my business logic there are currently 4 subclasses and in future there can be more and the logic becomes more clumsy.
Is there a better way of implementing the same in mapstruct?
Not sure if I understand correctly, but you just want to pick which subclass to map to based on a field in the source? Don't do that in the business logic, simply use a default method. Something like this:
#Mapper
public interface EntityMapper {
default BaseClass map(Entity entity) {
if(entity.getType().equals("a"))
return mapEntityToSubClass1(entity);
if(entity.getType().equals("b"))
return mapEntityToSubClass2(entity);
throw new RuntimeException("Unsupported base type");
}
Sub1 mapEntityToSubClass1(Entity entity);
Sub2 mapEntityToSubClass2(Entity entity);
}
I am currently making a service in which there are lots of public API's. And the response and request objects overlap a lot. So, I was thinking that is there a way by which we can generalise the pojo creation for the request/response objects.
Sometimes the response object is identical to the request object with one or two extra fields.
Let me give you an example.
#Data
public class Request {
private A objA;
private B objB;
}
#Data
public class Response {
private A objA;
private B objB;
private C objC;
}
#Data
public class A {
private D objD;
}
#Data
public class B {
private String sB;
private E obje;
}
#Data
public class C {
private String sC;
}
Similary, D and E are pojos as well. The thing is that there is a lot of similarity(overlapping fields) in request/response objects.
Your solution is probably inheritance: Create a parent abstract object type with the overlapping fields and have the request and response objects extend it and specify any extra (unique) fields they need.
Inheritence
public abstract class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request extends Common {
private String requestField1;
private String requestField2;
}
public class Response extends Common {
private String responseField1;
private String responseField2;
}
You could also approach this using composition: Create an object type with the overlapping fields and include this object as a sub-object of the Request/Response types:
Composition
public class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request {
private String requestField1;
private String requestField2;
private Common common;
}
public class Response {
private String responseField1;
private String responseField2;
private Common common;
}
There are pros and cons to each approach which are widely discussed on this and other boards. These however, are the two standard approaches to dealing with such a problem.
It really depends on what you are trying to achieve. I don't see it being a huge problem repeating the fields but you've given an abstract use case rather than a real world situation where I can understand what you're trying to achieve.
Perhaps you want to pass your #Data objects to the same services? In which case you might want to use interfaces because a class can implement multiple interfaces.
Eg
public interface AContiner {
A getA();
void setA(A a);
}
public interface BContiner {
B getB();
void setB(B b);
}
#Data
public class Bean1 implements AContainer {
private A a;
}
#Data
public class Bean2 implements AContainer, BContainer {
private A a;
private B b;
}
public class MyFantasticService {
public void doStuffWithA(AContainer data) {
System.out.println(data.getA());
}
public void doStuffWithB(BContainer data) {
System.out.println(data.getB());
}
}
I have a model.
public class Model {
#JsonProperty("model_id")
private Integer id;
#JsonView(face.Test.class)
#JsonProperty("model_name")
private String name;
#JsonProperty("model_level")
private Byte level;
#JsonView(face.New.class)
#JsonProperty("model_score")
private Byte score;
#JsonView(face.Test.Tester.class)
#JsonProperty("model_community")
private Long community;
//getter and setters
}
as you can see my id and level fields will be included in all JSONs which be created from this model.
now I want to put for example field level in all JSONs (which be created from this model) except one.
is there something like #JsonView(!face.Example.class) ? if it's not, what is the best solution for this case?
thanks so much
I believe you need to use view class inheritance to accomplish this.
public class face {
public static class BaseView {}
public static class Test extends BaseView {}
public static class New extends BaseView {}
public static class Example {} // no parent
}
And so:
public class Model {
// included in all views
private Integer id;
// only in Test view
#JsonView(face.Test.class)
private String name;
// included in BaseView and its children (ie not Example)
#JsonView(face.BaseView.class)
private Byte level;
// ...
}
Presumably you have a logical hierarchy of views that apply to more than just the one field.
The situation is this:
On the side of the domain we have a superclass let's call it Plant
and two subclasses Vegestable and DecorativePlant.
public abstract class Plant {
private String name;
#Mapping("elementValue.carbonValue")
private int carbonValue;
#Mapping("elementValue.oxygenValue")
private int oxygenValue;
}
public abstract class Vegestable extends Plant {
private int nutritionValue;
}
public abstract class DecorativePlant extends Plant {
private int rating;
}
now on the side of our soap api we have simular objects. The main difference
would probably be that we don't want the decorative plant on the soap-side to have
public class Vegestable {
private int nutrition;
private ElementValue elementValue;
}
public class DecorativePlant {
private int rating;
}
public class ElementValue {
private int carbonValue;
private int oxygenValue;
}
So now I was wondering if it is possible to specify that Dozer only maps the fields carbonValue and oxygenValue for subclasses of Vegestable and not for subclasses of DecorativePlant? If it's possible in Dozer than I won't have to actually alter my classes on the domain level and basically place the carbonValue and oxygenValue in both subclasses and than Dozer won't do the mapping for DecorativePlant. (A side from the #Mapping annotation I'm doing all my mapping in a mappings.xml file.)
Big thanks in advance!
I got 2 POJOs that are being passed to a HttpEntity and converted to json.
Before passing them i need a variable which is in both of the POJOs with different names because of the needs of the API so i cant change them.
What is the best way without casting and in terms of OOP also within the POJO definition like mentioned in Wikipedia?
Abstract pojo
public abstract class Pojo{
//some common variables
//setter getters
}
PojoOne
public class PojoOne extends Pojo{
private String id;
//setter getter for id
}
PojoTwo
public class PojoTwo extends Pojo{
private String identifier;
// setter getter for identifier
}
Class that
public class SomeOtherClass {
public void getIdForUse(Pojo pojo){
String s = pojo. // How should this be to have ability to get both id and identifier
}
}
Add a common method to the common superclass:
public abstract class Pojo {
public abstract String getId();
// some common variables
// setter getters
}
public class PojoOne extends Pojo {
private String id;
#Override
public String getId() {
return id;
}
//setter for id
}
public class PojoTwo extends Pojo {
private String identifier;
// setter getter for identifier
#Override
public String getId() {
return identifier;
}
}