开发者

Dozer deep propery mapping with custom converter

开发者 https://www.devze.com 2023-03-13 19:09 出处:网络
I have deep property mapping in my application (from domain objects to DTO, and the reverse), similar to next example:

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.

When Dozer converts DTO to Domain, it maps 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

0

精彩评论

暂无评论...
验证码 换一张
取 消