#java #mapstruct
Вопрос:
Я получаю неоднозначную ошибку отображения при «отображении с карты» с помощью MapStruct 1.3.1.Final
@Mapper( uses = MappingUtil.class )
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(source = "map", target = "aProperty", qualifiedBy = A.class )
@Mapping(source = "map", target = "bProperty", qualifiedBy = B.class )
Target toTarget(Map<String,Map<String, Object>> map);
}
public class MappingUtil {
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface A {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface B {
}
@A
public String abc(Map<String,Map<String, Object>> in) {
return (String) in.get("first_key").get("a_second_key");
}
@B
public String xyz(Map<String,Map<String, Object>> in) {
return (String) in.get("first_key").get("b_second_key");
}
}
Когда я изменяю @A или @B на другой тип, ошибка исчезает
@B
public int xyz(Map<String,Map<String, Object>> in) {
return (int) in.get("first_key").get("b_second_key");
}
Чего мне не хватает? В примере с документами использовались два метода одного и того же типа, поэтому это не должно быть проблемой
ИЗМЕНИТЬ: Та же проблема в Mapstruct 4.1.2.Финал
Комментарии:
1. Вы пробовали 1.4.2.Final?
2. Только что попробовал, та же проблема
Ответ №1:
Ваша реализация квалификаторов неверна. Вот ссылка, которая показывает, как реализовать пользовательское сопоставление с помощью квалификаторов в случае неоднозначных сопоставлений.
Ниже приведена краткая версия кода о том, как вы можете создавать пользовательские сопоставления на основе квалификаторов:
@Qualifier
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Translator {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface A {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface B {
}
@Translator
public class MappingUtil {
@A
public String abc(Map<String,Map<String, Object>> in) {
return (String) in.get("first_key").get("a_second_key");
}
@B
public String xyz(Map<String,Map<String, Object>> in) {
return (String) in.get("first_key").get("b_second_key");
}
}
@Mapper( uses = MappingUtil.class )
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(source = "map", target = "aProperty", qualifiedBy = { Translator.class, A.class } )
@Mapping(source = "map", target = "bProperty", qualifiedBy = { Translator.class, B.class } )
Target toTarget(Map<String,Map<String, Object>> map);
}
Альтернатива:
Вы можете использовать qualifiedByName
, если не хотите создавать отдельный служебный класс. Ниже приведен пример кода для этого:
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(source = "map", target = "aProperty", qualifiedByName= "abc" )
@Mapping(source = "map", target = "bProperty", qualifiedByName= "xyz" )
Target toTarget(Map<String,Map<String, Object>> map);
@Named("abc")
default String abc(Map<String,Map<String, Object>> in) {
// return whatever need to return
}
@Named("xyz")
default String xyz(Map<String,Map<String, Object>> in) {
// return whatever need to return
}
}
Комментарии:
1. Эта альтернатива работает! Было бы по-прежнему интересно узнать основную причину опубликованной проблемы, хотя
2. Интересно, я думаю, что пример в вопросе должен сработать. @helpme не могли бы вы, пожалуйста, поднять этот вопрос в проекте mapstruct, чтобы мы могли взглянуть на это?
3. @Филип, Должно быть, есть какая-то важность
Translator
класса, верно? Хотя я попробую оба вышеперечисленных сценария с моей стороны.4. Это многоуровневые квалификаторы. Один на вершине класса, а другой на методе, но он также должен работать и без него, в основном он должен быть идентичен
@Named
Ответ №2:
Похоже, это работает правильно при использовании 1.4.2.Final. Однако в версии 1.5.0.Beta1 это больше не работает из-за недавно введенной карты для сопоставления компонентов. Мы исследуем, как мы можем это исправить.
Однако есть альтернативное решение, которое позволит сделать эту работу правильной:
@Mapper( uses = MappingUtil.class )
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(source = "first_property", target = "aProperty", qualifiedBy = A.class )
@Mapping(source = "first_property", target = "bProperty", qualifiedBy = B.class )
Target toTarget(Map<String,Map<String, Object>> map);
}
public class MappingUtil {
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface A {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface B {
}
@A
public String abc(Map<String, Object> in) {
return (String) in.get("a_second_key");
}
@B
public String xyz(Map<String, Object> in) {
return (String) in.get("b_second_key");
}
}
Обратите внимание на изменение подписей в MappingUtil
и изменение в Mapping#source
.