无法让 MapStruct 与枚举继承和@TargetTytpe 一起工作
Can't get MapStruct to work with Enum Inheritance & @TargetTytpe
这是我的示例设置:
interface EnumMessage {
boolean carrier(String message);
String getValue();
default String getMessage() {
return getValue().toUpperCase();
}
}
enum EnumMessageA implements EnumMessage {
GREAT("You are doing great"),
GOOD("You are doing good"),
OK("You are doing ok");
private String value;
EnumMessageA(final String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
@Override
public boolean carrier(final String message) {
return this.value.equals(message);
}
}
class SourceX {
public String name;
public String message;
}
class TargetX {
public String name;
public EnumMessageA messageA;
}
@Mapper
interface EnumMapper {
EnumMapper INSTANCE = Mappers.getMapper(EnumMapper.class);
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
TargetX toTarget(SourceX source);
@Mapping(source = "messageA", target = "message", qualifiedByName = "enumToString")
SourceX toSource(TargetX target);
@Named("stringToEnum")
default <T extends EnumMessage> T mapStringToEnum(
final String message, @TargetType final Class<T> enumClass) {
final T[] values = enumClass.getEnumConstants();
return Arrays.stream(values)
.filter(enumValue -> enumValue.carrier(message))
.findFirst()
.orElse(null);
}
@Named("enumToString")
default <T extends EnumMessage> String mapEnumToString(final T enumValue) {
return enumValue.getMessage();
}
}
我无法通过测试。
public class EnumMapperTest {
@Test
void checkMapping() {
TargetX target = new TargetX();
target.name = "MapStructTesting";
target.messageA = EnumMessageA.GREAT;
Assertions.assertEquals(
"You are doing great".toUpperCase(), EnumMapper.INSTANCE.toSource(target).message);
SourceX source = new SourceX();
source.name = "MapStructTesting";
source.message = "You are doing ok";
Assertions.assertEquals(EnumMessageA.OK, EnumMapper.INSTANCE.toTarget(source).messageA);
}
}
我尝试过使用或不使用 qualifiedByName
,但无法让 MapStruct 使用 mapStringToEnum
方法。
我不确定这是否是 MapStruct 中的错误,在使用 @TargetType
时无法使用 Enum 继承,或者我遗漏了什么。
- 当使用
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
时,它给出编译时错误:
error: Qualifier error. No method found annotated with @Named#value: [ stringToEnum ]. See https://mapstruct.org/faq/#qualifier for more info.
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
error: Can't map property "String message" to "EnumMessageA messageA". Consider to declare/implement a mapping method: "EnumMessageA map(String value)".
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
- 当使用
@Mapping(source = "message", target = "messageA")
或 resultType @Mapping(source = "message", target = "messageA", resultType = EnumMessage.class
时,生成的实现会忽略提供的方法。
class EnumMapperImpl implements EnumMapper {
@Override
public TargetX toTarget(SourceX source) {
if ( source == null ) {
return null;
}
TargetX targetX = new TargetX();
if ( source.message != null ) {
targetX.messageA = Enum.valueOf( EnumMessageA.class, source.message );
}
targetX.name = source.name;
return targetX;
}
@Override
public SourceX toSource(TargetX target) {
if ( target == null ) {
return null;
}
SourceX sourceX = new SourceX();
sourceX.message = mapEnumToString( target.messageA );
sourceX.name = target.name;
return sourceX;
}
}
如果有更优雅的方法来概括映射,我愿意接受建议。
注意:我不能使用 @BeforeMapping / @AfterMapping
,因为我有许多要映射的枚举类型,因此正在寻找隐式或通用解决方案。
弄清楚了,并在 GitHub 上向 MapStruct 团队提出了一个错误。
要使其正常工作,必须满足以下条件:
- 映射器方法必须是
static
qualifiedByName
属性当然是必需的,即使我将通用签名更改为 <T extends Enum<T> & EnumMessage>
所以我修改后的代码如下所示:
@Mapper
public class EnumMapperHelper {
@Named("stringToEnum")
public static <T extends EnumMessage> T mapStringToEnum(
final String message, @TargetType final Class<T> enumClass) {
final T[] values = enumClass.getEnumConstants();
return Arrays.stream(values)
.filter(enumValue -> enumValue.carrier(message))
.findFirst()
.orElse(null);
}
@Named("enumToString")
//this doesn't have to be static but yes the @Qualifier is required
public static <T extends EnumMessage> String mapEnumToString(final T enumValue) {
return enumValue.getMessage();
}
}
@Mapper(uses = EnumMapperHelper.class)
interface EnumMapper {
EnumMapper INSTANCE = Mappers.getMapper(EnumMapper.class);
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
TargetX toTarget(SourceX source);
@Mapping(source = "messageA", target = "message", qualifiedByName = "enumToString")
SourceX toSource(TargetX target);
}
这是我的示例设置:
interface EnumMessage {
boolean carrier(String message);
String getValue();
default String getMessage() {
return getValue().toUpperCase();
}
}
enum EnumMessageA implements EnumMessage {
GREAT("You are doing great"),
GOOD("You are doing good"),
OK("You are doing ok");
private String value;
EnumMessageA(final String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
@Override
public boolean carrier(final String message) {
return this.value.equals(message);
}
}
class SourceX {
public String name;
public String message;
}
class TargetX {
public String name;
public EnumMessageA messageA;
}
@Mapper
interface EnumMapper {
EnumMapper INSTANCE = Mappers.getMapper(EnumMapper.class);
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
TargetX toTarget(SourceX source);
@Mapping(source = "messageA", target = "message", qualifiedByName = "enumToString")
SourceX toSource(TargetX target);
@Named("stringToEnum")
default <T extends EnumMessage> T mapStringToEnum(
final String message, @TargetType final Class<T> enumClass) {
final T[] values = enumClass.getEnumConstants();
return Arrays.stream(values)
.filter(enumValue -> enumValue.carrier(message))
.findFirst()
.orElse(null);
}
@Named("enumToString")
default <T extends EnumMessage> String mapEnumToString(final T enumValue) {
return enumValue.getMessage();
}
}
我无法通过测试。
public class EnumMapperTest {
@Test
void checkMapping() {
TargetX target = new TargetX();
target.name = "MapStructTesting";
target.messageA = EnumMessageA.GREAT;
Assertions.assertEquals(
"You are doing great".toUpperCase(), EnumMapper.INSTANCE.toSource(target).message);
SourceX source = new SourceX();
source.name = "MapStructTesting";
source.message = "You are doing ok";
Assertions.assertEquals(EnumMessageA.OK, EnumMapper.INSTANCE.toTarget(source).messageA);
}
}
我尝试过使用或不使用 qualifiedByName
,但无法让 MapStruct 使用 mapStringToEnum
方法。
我不确定这是否是 MapStruct 中的错误,在使用 @TargetType
时无法使用 Enum 继承,或者我遗漏了什么。
- 当使用
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
时,它给出编译时错误:
error: Qualifier error. No method found annotated with @Named#value: [ stringToEnum ]. See https://mapstruct.org/faq/#qualifier for more info.
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
error: Can't map property "String message" to "EnumMessageA messageA". Consider to declare/implement a mapping method: "EnumMessageA map(String value)".
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
- 当使用
@Mapping(source = "message", target = "messageA")
或 resultType@Mapping(source = "message", target = "messageA", resultType = EnumMessage.class
时,生成的实现会忽略提供的方法。
class EnumMapperImpl implements EnumMapper {
@Override
public TargetX toTarget(SourceX source) {
if ( source == null ) {
return null;
}
TargetX targetX = new TargetX();
if ( source.message != null ) {
targetX.messageA = Enum.valueOf( EnumMessageA.class, source.message );
}
targetX.name = source.name;
return targetX;
}
@Override
public SourceX toSource(TargetX target) {
if ( target == null ) {
return null;
}
SourceX sourceX = new SourceX();
sourceX.message = mapEnumToString( target.messageA );
sourceX.name = target.name;
return sourceX;
}
}
如果有更优雅的方法来概括映射,我愿意接受建议。
注意:我不能使用 @BeforeMapping / @AfterMapping
,因为我有许多要映射的枚举类型,因此正在寻找隐式或通用解决方案。
弄清楚了,并在 GitHub 上向 MapStruct 团队提出了一个错误。
要使其正常工作,必须满足以下条件:
- 映射器方法必须是
static
qualifiedByName
属性当然是必需的,即使我将通用签名更改为<T extends Enum<T> & EnumMessage>
所以我修改后的代码如下所示:
@Mapper
public class EnumMapperHelper {
@Named("stringToEnum")
public static <T extends EnumMessage> T mapStringToEnum(
final String message, @TargetType final Class<T> enumClass) {
final T[] values = enumClass.getEnumConstants();
return Arrays.stream(values)
.filter(enumValue -> enumValue.carrier(message))
.findFirst()
.orElse(null);
}
@Named("enumToString")
//this doesn't have to be static but yes the @Qualifier is required
public static <T extends EnumMessage> String mapEnumToString(final T enumValue) {
return enumValue.getMessage();
}
}
@Mapper(uses = EnumMapperHelper.class)
interface EnumMapper {
EnumMapper INSTANCE = Mappers.getMapper(EnumMapper.class);
@Mapping(source = "message", target = "messageA", qualifiedByName = "stringToEnum")
TargetX toTarget(SourceX source);
@Mapping(source = "messageA", target = "message", qualifiedByName = "enumToString")
SourceX toSource(TargetX target);
}