ModelMapper:如何映射作为通用传递的 List<String>?
ModelMapper: How to map List<String> which is passed as generic?
我有一个摘要class:
public abstract class AbstractBeanDTO<T> extends AbstractBigDTO {
protected T value;
//getter setter
}
扩展 class 具有通用类型 List
public class ItemsBeanDTO extends AbstractBeanDTO<List<String>> {
}
我正在尝试将模型 class 映射到也具有相同结构的 DTO。
AbstractBeanDTO<?> dto = (AbstractBeanDTO<?>) modelMapper.map(modelBean, ItemsBeanDTO.class);
我的 modelMapper 配置为严格匹配。它能够转换其他泛型类型,如 Long、Integer、String 但不能转换 List。
我收到以下错误:
org.modelmapper.MappingException: ModelMapper mapping errors:
1) Failed to instantiate instance of destination java.util.List. Ensure that java.util.List has a non-private no-argument constructor.
Caused by: java.lang.NoSuchMethodException: java.util.List.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at org.modelmapper.internal.MappingEngineImpl.instantiate(MappingEngineImpl.java:333)
at org.modelmapper.internal.MappingEngineImpl.createDestination(MappingEngineImpl.java:348)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:141)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:115)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:115)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:72)
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573)
at org.modelmapper.ModelMapper.map(ModelMapper.java:406)
我尝试使用 TypeMap 显式映射它,但它是一样的。
modelMapper.createTypeMap(ItemsBean.class, ItemsBeanDTO.class).addMappings(mapper -> {
mapper.map(src -> src.getValue(), (dest, value) -> dest.setValue((List<String>) value));
});
唯一有效的方法是将变量显式声明为 List。 (但我不想这样做)
我正在使用 2.3.5 版的 ModelMapper 和 Java8。
谁能帮我看看这是怎么回事?
我尝试如下使用 typeMap 和 typeToken,
Type listType = new TypeToken<ItemsBeanDTO>() {
}.getType();
modelMapper.createTypeMap(ItemsBean.class, ItemsBeanDTO.class).addMappings(mapper -> {
mapper.map(src -> src.getValue(), (dest, value) -> dest.setValue(modelMapper.map(value, listType)));
});
在这种情况下我得到以下错误:
java.lang.IllegalArgumentException: source cannot be null
at org.modelmapper.internal.util.Assert.notNull(Assert.java:53)
at org.modelmapper.ModelMapper.map(ModelMapper.java:493)
at com.lowteq.model.preop.controller.config.BeanModelMapperFactory.lambda(BeanModelMapperFactory.java:117)
at org.modelmapper.internal.ReferenceMapExpressionImpl.map(ReferenceMapExpressionImpl.java:68)
at org.modelmapper.internal.ConfigurableConditionExpressionImpl.map(ConfigurableConditionExpressionImpl.java:65)
at com.lowteq.model.preop.controller.config.BeanModelMapperFactory.lambda(BeanModelMapperFactory.java:117)
at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:266)
List 是一个接口,没有构造函数。 ModelMapper
需要 concrete class,因此您需要更喜欢 List
的实现,包括 ArrayList
或 LinkedList
等
我终于找到了解决这个问题的方法。您可以使用 typeMap 跳过 setter 方法设置列表并使用 setProvider 手动设置它。
TypeMap<ItemsBean, ItemsBeanDTO> typeMap = modelMapper.createTypeMap(ItemsBean.class,
ItemsBeanDTO.class);
typeMap.addMappings(mapper -> mapper.skip(ItemsBeanDTO::set));
typeMap.setProvider(request -> {
ItemsBean source = ItemsBean.class.cast(request.getSource());
ItemsBeanDTO destination = new ItemsBeanDTO();
destination.set(source.get());
return destination;
});
您可以改用我的图书馆 beanknife。它是一个注解处理器。这意味着它会即时生成 DTO class。您可以检查生成的来源class,不再魔法。转换器方法嵌入到生成的 class 中。它们并不复杂,但很麻烦,因此自动化是必要的。
您可以过滤从原始 class 继承的属性,甚至可以更改或添加新的 属性 到生成的 class。该库提供了一种仅通过注释 convert 和 属性 的方法。例如,如果 Integer 为 null,则将其转换为零。您也可以编写自己的实现。
例如:
class Pojo1 {
String a;
Pojo b;
}
class Pojo2 {
Pojo1 a;
List<Pojo1> b;
Map<List<Pojo1>>[] c;
}
// target Pojo1, and all properties will be included except 'b'
@ViewOf(value = Pojo1.class, includePattern = ".*", excludes={Pojo1Meta.b})
class ConfigureOfPojo2 {}
// target Pojo2, and all properties will be included
@ViewOf(value = Pojo2.class, includePattern = ".*")
class ConfigureOfPojo2 {
// convert b to dto version.
// Pojo1View is the generated dto class of Pojo1.
// Of course, you can change it. But here use the default name for simpify.
// If you want change the type or value of a existing property,
// You should use @OverrideViewProperty.
// Convert to dto version is internal supported.
// For more complex case, you may need to use a method to define how the property should be deal with.
@OverrideViewProperty(Pojo2Meta.b)
private List<Pojo1View> b;
// Convert c to dto version
// BeanKnife support convert list, set, map, array of object to its dto version, only if they has the same shape.
@OverrideViewProperty(Pojo2Meta.c)
private Map<List<Pojo1View>>[] c;
}
将生成
// meta class, you can use it to reference the property name in a safe way.
class Pojo1Meta {
public final String a = "a";
public final String b = "b";
}
// generated DTO class. The actual one will be more complicate, there are many other method.
class Pojo1View {
private String a;
public Pojo1View read(Pojo1 source) { ... }
... getters and setters ...
}
class Pojo2Meta {
public final String a = "a";
public final String b = "b";
public final String c = "c";
}
class Pojo2View {
private String a;
private List<Pojo1View> b;
private Map<List<Pojo1View>>[] c;
public Pojo2View read(Pojo2 source) { ... }
... getters and setters ...
}
还有更多examples
我有一个摘要class:
public abstract class AbstractBeanDTO<T> extends AbstractBigDTO {
protected T value;
//getter setter
}
扩展 class 具有通用类型 List
public class ItemsBeanDTO extends AbstractBeanDTO<List<String>> {
}
我正在尝试将模型 class 映射到也具有相同结构的 DTO。
AbstractBeanDTO<?> dto = (AbstractBeanDTO<?>) modelMapper.map(modelBean, ItemsBeanDTO.class);
我的 modelMapper 配置为严格匹配。它能够转换其他泛型类型,如 Long、Integer、String 但不能转换 List。
我收到以下错误:
org.modelmapper.MappingException: ModelMapper mapping errors:
1) Failed to instantiate instance of destination java.util.List. Ensure that java.util.List has a non-private no-argument constructor.
Caused by: java.lang.NoSuchMethodException: java.util.List.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at org.modelmapper.internal.MappingEngineImpl.instantiate(MappingEngineImpl.java:333)
at org.modelmapper.internal.MappingEngineImpl.createDestination(MappingEngineImpl.java:348)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:141)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:115)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:115)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:72)
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573)
at org.modelmapper.ModelMapper.map(ModelMapper.java:406)
我尝试使用 TypeMap 显式映射它,但它是一样的。
modelMapper.createTypeMap(ItemsBean.class, ItemsBeanDTO.class).addMappings(mapper -> {
mapper.map(src -> src.getValue(), (dest, value) -> dest.setValue((List<String>) value));
});
唯一有效的方法是将变量显式声明为 List。 (但我不想这样做)
我正在使用 2.3.5 版的 ModelMapper 和 Java8。 谁能帮我看看这是怎么回事?
我尝试如下使用 typeMap 和 typeToken,
Type listType = new TypeToken<ItemsBeanDTO>() {
}.getType();
modelMapper.createTypeMap(ItemsBean.class, ItemsBeanDTO.class).addMappings(mapper -> {
mapper.map(src -> src.getValue(), (dest, value) -> dest.setValue(modelMapper.map(value, listType)));
});
在这种情况下我得到以下错误:
java.lang.IllegalArgumentException: source cannot be null
at org.modelmapper.internal.util.Assert.notNull(Assert.java:53)
at org.modelmapper.ModelMapper.map(ModelMapper.java:493)
at com.lowteq.model.preop.controller.config.BeanModelMapperFactory.lambda(BeanModelMapperFactory.java:117)
at org.modelmapper.internal.ReferenceMapExpressionImpl.map(ReferenceMapExpressionImpl.java:68)
at org.modelmapper.internal.ConfigurableConditionExpressionImpl.map(ConfigurableConditionExpressionImpl.java:65)
at com.lowteq.model.preop.controller.config.BeanModelMapperFactory.lambda(BeanModelMapperFactory.java:117)
at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:266)
List 是一个接口,没有构造函数。 ModelMapper
需要 concrete class,因此您需要更喜欢 List
的实现,包括 ArrayList
或 LinkedList
等
我终于找到了解决这个问题的方法。您可以使用 typeMap 跳过 setter 方法设置列表并使用 setProvider 手动设置它。
TypeMap<ItemsBean, ItemsBeanDTO> typeMap = modelMapper.createTypeMap(ItemsBean.class,
ItemsBeanDTO.class);
typeMap.addMappings(mapper -> mapper.skip(ItemsBeanDTO::set));
typeMap.setProvider(request -> {
ItemsBean source = ItemsBean.class.cast(request.getSource());
ItemsBeanDTO destination = new ItemsBeanDTO();
destination.set(source.get());
return destination;
});
您可以改用我的图书馆 beanknife。它是一个注解处理器。这意味着它会即时生成 DTO class。您可以检查生成的来源class,不再魔法。转换器方法嵌入到生成的 class 中。它们并不复杂,但很麻烦,因此自动化是必要的。 您可以过滤从原始 class 继承的属性,甚至可以更改或添加新的 属性 到生成的 class。该库提供了一种仅通过注释 convert 和 属性 的方法。例如,如果 Integer 为 null,则将其转换为零。您也可以编写自己的实现。
例如:
class Pojo1 {
String a;
Pojo b;
}
class Pojo2 {
Pojo1 a;
List<Pojo1> b;
Map<List<Pojo1>>[] c;
}
// target Pojo1, and all properties will be included except 'b'
@ViewOf(value = Pojo1.class, includePattern = ".*", excludes={Pojo1Meta.b})
class ConfigureOfPojo2 {}
// target Pojo2, and all properties will be included
@ViewOf(value = Pojo2.class, includePattern = ".*")
class ConfigureOfPojo2 {
// convert b to dto version.
// Pojo1View is the generated dto class of Pojo1.
// Of course, you can change it. But here use the default name for simpify.
// If you want change the type or value of a existing property,
// You should use @OverrideViewProperty.
// Convert to dto version is internal supported.
// For more complex case, you may need to use a method to define how the property should be deal with.
@OverrideViewProperty(Pojo2Meta.b)
private List<Pojo1View> b;
// Convert c to dto version
// BeanKnife support convert list, set, map, array of object to its dto version, only if they has the same shape.
@OverrideViewProperty(Pojo2Meta.c)
private Map<List<Pojo1View>>[] c;
}
将生成
// meta class, you can use it to reference the property name in a safe way.
class Pojo1Meta {
public final String a = "a";
public final String b = "b";
}
// generated DTO class. The actual one will be more complicate, there are many other method.
class Pojo1View {
private String a;
public Pojo1View read(Pojo1 source) { ... }
... getters and setters ...
}
class Pojo2Meta {
public final String a = "a";
public final String b = "b";
public final String c = "c";
}
class Pojo2View {
private String a;
private List<Pojo1View> b;
private Map<List<Pojo1View>>[] c;
public Pojo2View read(Pojo2 source) { ... }
... getters and setters ...
}
还有更多examples