Dozer deep propery mapping with custom converter
I have deep property mapping in my application (from domain objects to DTO, and the reverse), similar to next example:
...
<field>
<a>employee.id</a>
<b>employeeId</a>
</field>
...
When Dozer converts Domain to DTO, it maps employee.id
to employeeId
, and that is ok.
employeeId
to a new Employee instance with id=employeeId.
I want to create some logic for this deep property mapping, but i just can't figure out solution. I tried to implement CustomConverter
(or extend DozerConverter
) but Dozer passes me Integer type as source and destination class(and expect Integer as result).
EDIT:
More precisely, what i need is to map employee
in Domain to null
if employeeId
in DTO is 0.
Is this possible?
Any advice?
EDIT ACCORDING TO ANSWERS: I solve problem with field-level custom converter. Instead of earlier, above mentioned, mapping, now i have something like this...
...
<field custom-converter="ManyToOneIdMapper" custom-converter-param="id">
<a>employee</a>
<b>employeeId</b>
</field>
...
In ManyToOneIdMapper i have...
public class ManyToOneIdMapper implements ConfigurableCustomConverter{
//...
//parameter field declaration, setParameter and getParameter implementations etc.
//...
public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue,
Class<?> destinationClass, Class<?> sou开发者_StackOverflow社区rceClass) {
try {
if(sourceClass.equals(Integer.class)){
Integer src=(Integer)sourceFieldValue;
if(src==null || src==0)
return null;
String setterName=formatMethodName("set", getParameter());
Method setterMethod=destinationClass.getMethod(setterName, Integer.class);
Object instance=destinationClass.newInstance();
setterMethod.invoke(instance, src);
return instance;
}else{
if(sourceFieldValue==null)
return 0;
String getterName=formatMethodName("get", getParameter());
Method getterMethod=sourceClass.getMethod(getterName);
Object instance=getterMethod.invoke(sourceFieldValue);
return instance;
}
} catch (Exception e){}
return null;
}
/**
* @return - method name (most often setter or getter) according to fieldName.
* For example formatMethodName("get", "id") returns "getId"
*/
protected String formatMethodName(String methodPrefix, String fieldName){
String trimmedFieldName=fieldName.trim();
String firstLetter=String.valueOf(trimmedFieldName.charAt(0));
String capitalizedFirstLetter=firstLetter.toUpperCase();
String methodName=methodPrefix+""+capitalizedFirstLetter+""+fieldName.substring(1);
return methodName;
}
custom-converter-param
is just name of id-field in Domain object. With that name, i just call setter or getter method in my converter. Probably, it is not the happiest solution, but it works for my problem scenario.
You can either use a CustomConverter
(per the other answer), or use a DozerEventListener
to set the employee object back to null after the mapping has finished, if the ID is 0.
You want a CustomConverter
to map the parent object, for example:
Domain
class PersonA {
...
int employeeId;
...
}
DTO
class PersonB {
...
Employee employee;
...
}
class Employee {
...
int id;
...
}
You want to map the two classes PersonA
and PersonB
using a CustomConverter
, this will let you construct them what ever way you want.
You might check out ModelMapper (author here). It will intelligently map the scenario you describe without requiring any configuration or custom converters. Considering the model:
class Person {
Employee employee;
}
class Employee {
int id;
}
class PersonDTO {
int employeeId;
}
Mapping is simple:
ModelMapper modelMapper = new ModelMapper();
PersonDTO personDTO = modelMapper.map(person, PersonDTO.class);
To conditionally map Person.employee
when PersonDTO.employeeId
is not zero, we simply create a condition and add a property mapping for Person.employee with the condition:
Condition<?, ?> empIdIsNotZero = new Condition<PersonDTO, Employee>() {
public boolean applies(MappingContext<PersonDTO, Employee> context) {
return context.getSource().getEmployeeId() != 0;
}
};
modelMapper.addMappings(new PropertyMap<PersonDTO, Person>() {
protected void configure() {
when(empIdIsNotZero).map(source).setEmployee(null);
}
});
When the empIdIsNotZero
condition applies, mapping will occur as normal. Otherwise mapping will be skipped and Person.employee
will be set to null.
Check out the ModelMapper site for more docs and examples:
http://modelmapper.org
精彩评论