NullPointerException when using withCustom
onoph opened this issue · 1 comments
Hi,
I have a problem on a helloworld example. I am trying to convert two nested beans to their respectful Dto. So I have created two mappers, one for each class and declared the child mapper in the parent like this :
@Mapper(withCustom = ChildMapper.class)
And I get the following exception at runtime (compilation is ok) :
java.lang.NullPointerException: null
at com.selmademo.selma.UserMapperSelmaGeneratedClass.asUserResource(UserMapperSelmaGeneratedClass.java:13)
at com.selmademo.selma.UserMapperTest.test_mapping_user_ok(UserMapperTest.java:23)
test_mapping_user_resource_ok(com.selmademo.selma.UserMapperTest) Time elapsed: 0 sec <<< ERROR!
java.lang.NullPointerException: null
at com.selmademo.selma.UserMapperSelmaGeneratedClass.asUser(UserMapperSelmaGeneratedClass.java:28)
at com.selmademo.selma.UserMapperTest.test_mapping_user_resource_ok(UserMapperTest.java:38)
Here is the source code of the entities, Dto (called resources), the mapper and the test files (sorry if code styling is not ok, I've never had success with github code style)
public class User {
private Long userId;
private String username;
private String password;
private Address address;
private LocalDate entryDate;
//.... getters/setters
}
public class Address {
private Long addressId;
private Integer number;
private String street;
private String zipCode;
//...getters/setters
}
public class UserResource {
private Long techId;
private String username;
private String password;
private AddressResource address;
private LocalDate entryDate;
//getters/setters...
public class AddressResource {
private Long techId;
private Integer number;
private String street;
private String zipCode;
//getters/setters
@Mapper(
withCustomFields = {
@field({"userId", "techId"})}
)
public interface UserMapper {
@Maps(withCustom = AddressMapper.class)
UserResource asUserResource(User user);
@Maps(withCustom = AddressMapper.class)
User asUser(UserResource userResource);
}
`
@Mapper(withCustomFields = {
@field({"addressId", "techId"})
})
public interface AddressMapper {
public Address asAddress(AddressResource in);
public AddressResource asAddressResource(Address in);
}
@test
public void test_mapping_user_ok(){
User user = getUser();
UserMapper mapper = Selma.mapper(UserMapper.class);
UserResource userResource = mapper.asUserResource(user); //fail here
//...
}
@test
public void test_mapping_user_resource_ok(){
UserResource userResource = getuserResource();
UserMapper mapper = Selma.mapper(UserMapper.class);
User user = mapper.asUser(userResource); //fail here too
//...
}
And the generated classes :
public final class UserMapperSelmaGeneratedClass
implements UserMapper {
@OverRide
public final com.selmademo.resource.UserResource asUserResource(com.selmademo.domain.User inUser) {
com.selmademo.resource.UserResource out = null;
if (inUser != null) {
out = new com.selmademo.resource.UserResource();
out.setAddress(customMapperAddressMapper.asAddressResource(inUser.getAddress()));
out.setEntryDate(inUser.getEntryDate());
out.setPassword(inUser.getPassword());
out.setTechId(inUser.getUserId());
out.setUsername(inUser.getUsername());
}
return out;
}
@OverRide
public final com.selmademo.domain.User asUser(com.selmademo.resource.UserResource inUserResource) {
com.selmademo.domain.User out = null;
if (inUserResource != null) {
out = new com.selmademo.domain.User();
out.setAddress(customMapperAddressMapper.asAddress(inUserResource.getAddress()));
out.setEntryDate(inUserResource.getEntryDate());
out.setPassword(inUserResource.getPassword());
out.setUserId(inUserResource.getTechId());
out.setUsername(inUserResource.getUsername());
}
return out;
}
/**
- This field is used for custom Mapping
*/
private AddressMapper customMapperAddressMapper;
/**
- Custom Mapper setter for customMapperAddressMapper
*/
public final void setCustomMapperAddressMapper(AddressMapper mapper) {
this.customMapperAddressMapper = mapper;
}
/**
- Single constructor
*/
public UserMapperSelmaGeneratedClass() {
}
}
public final class AddressMapperSelmaGeneratedClass
implements AddressMapper {
@OverRide
public final com.selmademo.domain.Address asAddress(com.selmademo.resource.AddressResource inAddressResource) {
com.selmademo.domain.Address out = null;
if (inAddressResource != null) {
out = new com.selmademo.domain.Address();
out.setNumber(inAddressResource.getNumber());
out.setStreet(inAddressResource.getStreet());
out.setAddressId(inAddressResource.getTechId());
out.setZipCode(inAddressResource.getZipCode());
}
return out;
}
@OverRide
public final com.selmademo.resource.AddressResource asAddressResource(com.selmademo.domain.Address inAddress) {
com.selmademo.resource.AddressResource out = null;
if (inAddress != null) {
out = new com.selmademo.resource.AddressResource();
out.setTechId(inAddress.getAddressId());
out.setNumber(inAddress.getNumber());
out.setStreet(inAddress.getStreet());
out.setZipCode(inAddress.getZipCode());
}
return out;
}
/**
- Single constructor
*/
public AddressMapperSelmaGeneratedClass() {
}
}
Hi,
you should provide the AddressMapper instance when building the UserMapper.
@test
public void test_mapping_user_ok(){
User user = getUser();
UserMapper mapper = Selma.builder(UserMapper.class)
.withCustom(Selma.mapper(AddressMapper.class)).build();
UserResource userResource = mapper.asUserResource(user); // No more fail here :)
//...
}
Also to simplify your UserMapper and avoid duplication you can use the WithCustom directly in @Mapper :
@Mapper(
withCustomFields = {
@field({"userId", "techId"})},
withCustom = AddressMapper.class
)
public interface UserMapper {
UserResource asUserResource(User user);
User asUser(UserResource userResource);
}
Hope this helps