Reputation: 1797
I have a User ID class, that simply contains a String. My API exposes this as a String, so I want to 'unwrap' this value using MapStruct.
Example:
record UserId(String id) {}
@Mapper
public abstract class UserMapper {
@Mapping(target = ".", source="userId.id")
protected abstract String toApi(UserId userId);
}
I'm expecting this to work, even pointing out to MapStruct which member to use for the target value. The mapper should simply pass the String contained in UserId
and pass it as an output. But instead MapStruct creates a new empty String and returns it without interacting with the source member (besides null-checks):
(generated code)
@Override
protected String toApi(UserId id) {
if ( id == null ) {
return null;
}
String string = new String();
return string;
}
This is obviously wrong, I don't want a new empty string, I want the string contained in UserId. I can't figure out the right annotations to get this to work. Any thoughts?
My workaround listed below is writing a custom mapping method that does just this, but it seems weird that there's no way to let MapStruct generate this code for me.
@Mapper
public abstract class UserMapper {
// ... other mapping methods ...
protected String toApi(Userid userId) {
return userId.id();
}
}
Upvotes: 4
Views: 337
Reputation: 31
You can use the expression attribute of @Mapping to directly call the getter method:
@Mapper
public abstract class UserMapper {
@Mapping(target = ".", expression = "java(userId.id())")
public abstract String toApi(UserId userId);
}
target = "."
tells MapStruct that the target is not a field but the object itself.
expression = "java(userId.id())"
let you use java code calling the id() method on the userId object.
Upvotes: 1
Reputation: 44476
As far as I know, this is not possible. MapStruct is used for field-to-field mapping (with accompanying, and String
has no accessible fields to be mapped on.
The only solution I am aware of is to manually write the implementation.
default String toApi(UserId userId) {
return userId.id();
}
If there is a possibility to implement an interface with a single method String id()
and make it implement all the records to be mapped in the same way, the mapper becomes more flexible:
interface Identifiable {
String id();
}
record UserId(String id) implements Identifiable {}
record UserId2(String id) implements Identifiable {}
default String toApi(Identifiable identifiable) {
return identifiable.id();
}
This the same principle can be used with enum
and the Enum
class:
default String enumToName(Enum<?> enumerable) {
return enumerable.name();
}
Upvotes: 0