前言
我们在使用EasyExcel做导入功能时,通过在实体或者VO加@ExcelProperty(“测试”)注解来实现列的一一对应,read()之后获取一个List<T>的结果集,得到java的数据类型然后在进行业务操作 但EasyExcel这种通过注解的方式实现导入就必须使用事先定义好数据类型来接收,它不支持对象中的子对象,也不能导入excel带有动态列的数据(即相同列名下的多个数据列) 换做java的数据结构即如下结构: 这里使用原生的EasyExcel并不能接收到多个列名为 测试1 的数据 这里提供一种解决方式,它能够实现动态列的解析,实例化对象中的子对象这种数据结构(仅支持二级的嵌套,即一对多的这种关系)
一、代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDynamic {
}
public class DynamicReadListener extends AnalysisEventListener<Map<Integer, ReadCellData<?>>> {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final List<Map<Integer, String>> headList = new ArrayList<>();
private final List<Map<Integer, String>> dataList = new ArrayList<>();
private final List<List<Integer>> dynamicIndex = new ArrayList<>();
private final List<Map<String, List<Object>>> dynamicColumns = new ArrayList<>();
public static DynamicReadListener init(){
return new DynamicReadListener();
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
log.info("解析到表头:{}", JsonUtil.toJson(headMap));
headList.add(headMap);
dynamicIndex.add(CollectUtil.repeatNode(headMap));
}
@Override
public void invoke(Map<Integer, ReadCellData<?>> data, AnalysisContext context) {
ReadSheetHolder holder = context.readSheetHolder();
try {
handleDynamicColumns(data, holder.getHeadRowNumber() - 1, holder.getRowIndex() - 1, headList.size());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
private void handleDynamicColumns(Map<Integer, ReadCellData<?>> data, Integer headRowIndex, Integer dataRowIndex, Integer columnSize) throws NoSuchFieldException, IllegalAccessException {
if (!dynamicIndex.isEmpty()){
Map<String, List<Object>> columns = new HashMap<>(columnSize);
Map<Integer, String> targetHead = headList.get(headRowIndex);
for (Integer targetIndex : dynamicIndex.get(headRowIndex)) {
String head = targetHead.get(targetIndex);
String value = data.get(targetIndex).getStringValue();
if (Objects.isNull(columns.get(head))){
columns.put(head, ListUtil.asList(value));
}else {
columns.get(head).add(value);
}
}
dynamicColumns.add(columns);
}
}
public List<Map<Integer, String>> getHeadList() {
return headList;
}
public List<Map<Integer, String>> getDataList() {
return dataList;
}
public List<Map<String, List<Object>>> getDynamicColumns() {
return dynamicColumns;
}
}
public class Excels extends EasyExcel{
private Excels() {}
public static <T> List<T> imports(InputStream fileStream, Class<T> clazz){
return read(fileStream).head(clazz).sheet().doReadSync();
}
public static <T> List<T> importsDynamic(InputStream fileStream, Class<T> clazz) throws IllegalAccessException, InstantiationException {
DynamicReadListener dynamicReadListener = DynamicReadListener.init();
SyncReadListener syncReadListener = new SyncReadListener();
ExcelReaderSheetBuilder sheet = read(fileStream).useDefaultListener(false).head(clazz).sheet();
sheet.registerReadListener(dynamicReadListener);
sheet.registerReadListener(new ModelBuildEventListener());
sheet.registerReadListener(syncReadListener);
sheet.doRead();
return build((List<T>) syncReadListener.getList(), dynamicReadListener);
}
private static <T> List<T> build(List<T> targets, DynamicReadListener listener) throws IllegalAccessException, InstantiationException {
if (CollectionUtils.isNotEmpty(targets)){
for (int i = 0; i < targets.size(); i++) {
T target = targets.get(i);
for (Field targetField : target.getClass().getDeclaredFields()) {
if (Objects.nonNull(targetField.getAnnotation(ExcelDynamic.class))){
targetField.setAccessible(true);
targetField.set(target, build(listener.getDynamicColumns().get(i), targetField.getType()));
}
}
}
}
return targets;
}
private static <T> T build(Map<String, List<Object>> dynamicRow, Class<T> clazz) throws InstantiationException, IllegalAccessException {
if (Objects.isNull(clazz)){
throw new NullPointerException("class not support null value");
}
Field[] fields = clazz.getDeclaredFields();
Map<Field, List<Object>> hitColumn = new HashMap<>(fields.length);
dynamicRow.forEach((head, columns)->{
for (Field field : fields) {
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
if (Objects.nonNull(annotation)){
Object[] annotationKeys = annotation.value();
for (Object annotationKey : annotationKeys) {
if (!Objects.equals("", annotationKey) && Objects.equals(head, annotationKey)){
if (CollectionUtils.isNotEmpty(columns)){
hitColumn.put(field, columns);
break;
}
}
}
}
}
});
return handle(hitColumn, clazz);
}
private static <T> T handle(Map<Field, List<Object>> hitColumn, Class<T> clazz) throws InstantiationException, IllegalAccessException {
if(CollectionUtils.isNotEmpty(hitColumn.keySet())){
T target = clazz.newInstance();
hitColumn.forEach((field, columns) -> {
try {
List<Object> targetFieldList = new ArrayList<>();
for (Object column : columns) {
try {
Object targetValue = Objects.requireNonNull(getGenericClass(field)).getConstructor(String.class).newInstance((String)column);
targetFieldList.add(targetValue);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
field.setAccessible(true);
field.set(target, targetFieldList);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return target;
}
return null;
}
private static Class<?> getGenericClass(Field field){
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
return (Class<?>) actualTypeArgument;
}
}
return null;
}
}
二、使用
文件数据
测试
@Test
public void test1() throws FileNotFoundException, IllegalAccessException, InstantiationException {
List<A> as = Excels.importsDynamic(new FileInputStream(new File("C:\\Users\\caobinghui\\Desktop\\test.xlsx")), A.class);
as.forEach(System.out::println);
}
结果
二、结束
代码拙劣,大家可以自己去试着优化 觉得有帮助的可以点个赞,谢谢!!
|