Search code examples
javadozer

How can I use dozer for Set<Object> mapping Set<String>, while Set<String> contains obj.getProperty()


How can I use dozer for field Set<Object> mapping Set<String>, while Set<String> contains obj.getProperty().

I want to map User.roleSet to UserVO.roleNames, which contains Role.name.

public class User {

   private Integer id;

   private Set<Role> roleSet;
}

public class UserVO {

    private Integer id;

    private Set<String> roleNames;
}

public class Role {

    private Integer id;

    private String name;
}

Solution

  • There is a couple of ways to deal with the problem but the first thing that you have to know about that is mapping between collections is little problematic due to Java generics. They are not available on the runtime and you have to be aware of it.

    So in this case on the runtime you will have to collections and based on the collection type you won't be able to determine the collection source type (only by checking some element it will be available).

    In this case I think the best approach would be define you own custom converter (which you have to register on the dozer config file within custom-converters tags). That part will look like something like that:

    <configuration>
    <custom-converters>
      <converter type="ToRoleNameConverter">
        <class-a>Role</class-a>
        <class-b>java.lang.String</class-b>
      </converter>
    </custom-converters>
    

    The source code of that converter:

    public class ToRoleNameConverter extends DozerConverter<Role, String> {
    
        @SuppressWarnings("unchecked")
        public ToRoleNameConverter() {
           super(Role.class, String.class);
        }
    
        @Override
        public String convertTo(Role source, String destination) {
            return source.getName();
        }
    
        @Override
        public Role convertFrom(String source, Role destination) {
           throw new UnsupportedOperationException("Unsupported operation exception!");
       }
    }
    

    With that converter you could use define how your basic class and embedded collection should be mapped. Additional dozer configuration will be needed:

    <mapping>
      <class-a>User</class-a>
      <class-b>UserDto</class-b>
      <field>
        <a>roles</a>
        <b>roleNames</b>
        <a-hint>Role</a-hint>
        <b-hint>java.lang.String</b-hint>
      </field>
    </mapping>
    

    With the given configuration you should try to map:

    User user = new User()
    user.setUserId(1)
    user.setRoles(Sets.newHashSet(new Role(1, "admin"), new Role(2, "manager")))
    
    UserDto map = mapper.map(user, UserDto.class)
    

    And get the results:

    User{id=1, roles=[Role{id=2, name=manager}, Role{id=1, name=admin}]}
    UserDto{id=1, roleNames=[manager, admin]}
    

    Hope that explain your question!