9 回答
在 Spring 中转换 fromList<A>
的另一种方法List<B>
是使用ConversionService#convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
. Javadoc
这种方法只需要一个Converter<A,B>
.
为集合类型调用转换服务:
List<A> source = Collections.emptyList();
TypeDescriptor sourceType = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(A.class));
TypeDescriptor targetType = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(B.class));
List<B> target = (List<B>) conversionService.convert(source, sourceType, targetType);
转换器:
public class ExampleConverter implements Converter<A, B> {
@Override
public B convert(A source) {
//convert
}
}
我遇到了同样的问题,并通过进行一些调查找到了解决方案(对我有用)。如果您有两个类 A 和 B,并且有一个已注册的转换器,例如 SomeConverter 实现了转换器,那么要将 A 列表转换为 B 列表,您接下来应该执行以下操作:
List<A> listOfA = ...
List<B> listOfB = (List<B>)conversionService.convert(listOfA,
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(A.class)),
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(B.class)));
拉尔夫是正确的,如果你有一个从A
To转换的转换器,它就可以工作B
。
我不需要 to 的转换List<A>
器List<B>
。
我有一个解决方法给你。这不是世界上最漂亮的东西,但如果使用 Spring 转换服务对您很重要,它可能会成功。
正如您所指出的,问题是类型擦除。您可以通过 ConversionService 接口告诉 Spring 的最好方法是您可以将 List 转换为 List,这对 Spring 没有意义,这就是转换器不起作用的原因(这是我的猜测..我不认为这是换句话说,错误)。
要执行您想要的操作,您需要创建和使用通用接口和/或类的特定于类型的实现:
public interface StringList extends List<String> { }
public class StringArrayList extends ArrayList<String> implements StringList { }
在此示例中,您将使用 StringArrayList 而不是 ArrayList 创建列表,并通过 ConversionService 接口注册实现类 (StringArrayList.class) 或接口类 (StringList.class)。似乎您想注册接口..但如果您只想注册实现类,那么您根本不需要定义接口。
我希望这有帮助。
我在推土机上遇到了同样的问题,发现了这个: How to map collections in dozer
因此,为了使用 spring 转换服务来做到这一点,我编写了一个简单的实用方法:
public static <T, U> List<U> convertList(final ConversionService service, final List<T> source, final Class<U> destType) {
final List<U> dest = new ArrayList<U>();
if (source != null) {
for (final T element : source) {
dest.add(service.convert(element, destType));
}
}
return dest;
}
本质上唯一的区别是它接受了一个弹簧转换服务而不是一个推土机映射器实例。您现在可以使用此方法来转换具有类型安全性的 List。这与上面 Jeff B 的回答类似,只是您不需要禁止显示警告,并且此方法不一定需要属于给定的 Convert ...它是一种实用方法。
而且,如果您已经超越了 Java 7,那么总有一种方法可以使用流:
List<B> listB = listA.stream().map(a -> converterService.convert(a, B.class)).collect(Collectors.toList());
我发现一个相对干净的解决方法是创建一个代理转换器类,它接受要转换的原始对象并委托给支持boolean canConvert
语义的 Converter 接口的扩展版本,以便允许实现决定它是否可以是否转换该源数据。
例如:
界面:
public interface SqlRowSetToCollectionConverter<T> extends Converter<SqlRowSet,Collection<T>> {
boolean canConvert (SqlRowSet aSqlRowSet);
}
代理类:
public class SqlRowSetToCollectionConverterService implements Converter<SqlRowSet, Collection<?>> {
private SqlRowSetToCollectionConverter<?>[] converters;
public void setConverters (SqlRowSetToCollectionConverter<?>[] aConverters) {
converters = aConverters;
}
@Override
public Collection<?> convert (SqlRowSet aSource) {
for (SqlRowSetToCollectionConverter<?> converter : converters) {
if (converter.canConvert (aSource)) {
return (converter.convert(aSource));
}
}
return null;
}
}
然后我会向 Spring 的 Conversion Service 注册代理类,并向代理类注册所有扩展接口实现:
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.hilton.hms.data.convert.SqlRowSetToCollectionConverterService">
<property name="converters">
<bean class="com.hilton.hms.data.convert.SqlRowSetToConfigCollectionConverter" />
</property>
</bean>
</set>
</property>
</bean>
GenericConversionService 现在有方法
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor targetType) {
return convert(source, TypeDescriptor.forObject(source), targetType);
}
所以现在,我们只能传递一个 TypeDescriptor
public <S, @NonNull T> Collection<T> convert(Collection<S> source, Class<T> targetType) {
return (Collection<@NonNull T>) requireNonNull(conversionService.convert(source, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(targetType))));
}