Easy excel实战:读取文件自定义转化器不生效问题处理方案

    1.场景问题说明
    2.问题处理过程分析

1.场景问题说明
    现有一文件读取需求,需要将员工信息中的状态(正常或离职)进行翻译对应的数值,但是自定义转化器之后不生效。现将问题处理过程进行简单记录,希望能帮助到遇到同样问题的同学。
    待读取的原始文件:
在这里插入图片描述
    添加依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.8</version></dependency>

    自定义格式转化器PersonalConvert:

public class PersonalConvert implements Converter<String> {// 指定转化参数的Java类型@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}// 指定转化参数对应的单元格类型@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}// 单元格参数类型转化为Java类型处理逻辑@Overridepublic String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return "正常".equals(cellData.getStringValue())  ? "1":"2";}
}

    excel对应的实体类CustomPerson :

public class CustomPerson {private String name;private int age;@MobileValidprivate String mobile;@DateTimeFormat("yyyy-MM-dd")  // 官方提供的日期格式转化方式private String createTime;@ExcelProperty(value = "状态(1.正常;2.离职)"",converter = PersonalConvert.class)private String status;// 省略get/set
}	

    文件读取处理:

public static void main(String[] args) {ArrayList<CustomPerson> personArrayList = new ArrayList<CustomPerson>();EasyExcel.read("F:\\test.xls", CustomPerson.class, new PersonalListener<CustomPerson>(// 监听器中doAfterAllAnalysed执行此方法;所有读取完成之后处理逻辑personList->{for (CustomPerson person:personList){personArrayList.add(person);}})).sheet().doRead();  // 读取文件操作System.out.println(personArrayList);}

    输出内容:

2022-05-04 21:20:28.331 DEBUG [main] com.alibaba.excel.context.AnalysisContextImpl:96 - Began to readcom.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder@74395b9a
[CustomPerson{name='张三', age=1, mobile='18523568956', createTime='2022-05-01', status=null}, CustomPerson{name='李四', age=2, mobile='12345678911', createTime='2022-05-02', status=null}, CustomPerson{name='王五', age=3, mobile='18523568956', createTime='2022-05-03', status=null}]

    发现状态参数并没有转化成对应的数值,自定义转化器不生效!如果想要实现参数值转化,也可以不借助excel自带的参数转化功能,在读取的CustomPerson集合信息后自己进行参数类型转化。当然easy excel支持参数类型转化就看一下不生效原因。

2.问题处理过程分析
    翻看源码发现读取字段转换主要实现逻辑在于:
ModelBuildEventListener.javabuildUserModel,读取每行数据并组装每行的结果模型。
主要看一下每行字段是如何进行解析的。

private Object buildUserModel(Map<Integer, ReadCellData<?>> cellDataMap, ReadSheetHolder readSheetHolder,AnalysisContext context) {// 省略部分代码Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();BeanMap dataMap = BeanMapUtils.create(resultModel);// 获取所有的表头信息组合for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {Integer index = entry.getKey();Head head = entry.getValue();String fieldName = head.getFieldName();if (!cellDataMap.containsKey(index)) {continue;}ReadCellData<?> cellData = cellDataMap.get(index);// 利用转化器进行数据格式转化Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(),ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(),fieldName), readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(), index);if (value != null) {dataMap.put(fieldName, value);}}// 解析之后:CustomPerson{name='张三', age=1, mobile='18523568956', createTime='2022-05-01', status=null},status那列未做解析。return resultModel;}

debug调试在这里插入图片描述
    那就看headMap为什么只有四列,主要看ExcelHeadProperty.javainitOneColumnProperty

 private void initOneColumnProperty(int index, Field field, Boolean forceIndex) {ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);List<String> tmpHeadList = new ArrayList<String>();String fieldName = FieldUtils.resolveCglibFieldName(field);boolean notForceName = excelProperty == null || excelProperty.value().length <= 0|| (excelProperty.value().length == 1 && StringUtils.isEmpty((excelProperty.value())[0]));if (headMap.containsKey(index)) {tmpHeadList.addAll(headMap.get(index).getHeadNameList());} else {if (notForceName) {tmpHeadList.add(fieldName);} else {Collections.addAll(tmpHeadList, excelProperty.value());}}Head head = new Head(index, field, fieldName, tmpHeadList, forceIndex, !notForceName);headMap.put(index, head);}

DefaultAnalysisEventProcessorbuildHead

private void buildHead(AnalysisContext analysisContext, Map<Integer, ReadCellData<?>> cellDataMap) {// 省略部分代码for (Map.Entry<Integer, Head> entry : headMapData.entrySet()) {Head headData = entry.getValue();// 实体类中没有@ExcelProperty注解的成员变量或是有@ExcelProperty但value属性为空或不指定的成员变量都会添加到tmpHeadMap中,最终组装到headMap中。forceIndex和forceName如何进行设置值,参考下面debug截图if (headData.getForceIndex() || !headData.getForceName()) {tmpHeadMap.put(entry.getKey(), headData);continue;}List<String> headNameList = headData.getHeadNameList();String headName = headNameList.get(headNameList.size() - 1);// 实现的主要逻辑是对于标注@ExcelProperty的value属性,与excel表中的表头进行遍历匹配,如果@ExcelProperty中value属性与表头一致则添加到tmpHeadMap中,最终组装到headMap中for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) {if (stringEntry == null) {continue;}String headString = stringEntry.getValue();Integer stringKey = stringEntry.getKey();if (StringUtils.isEmpty(headString)) {continue;}if (analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) {headString = headString.trim();}// @ExcelProperty中value属性与表头一致则添加到tmpHeadMap中if (headName.equals(headString)) {headData.setColumnIndex(stringKey);tmpHeadMap.put(stringKey, headData);break;}}}excelHeadPropertyData.setHeadMap(tmpHeadMap);}

    headData.getForceName()forceName属性获取过程源码分析:在这里插入图片描述
    继续往下看:
在这里插入图片描述
    从中可以得出结论:实体类属性标注ExcelProperty注解但是value不指定或为空或是value值必须和excel表头内容相同才可以支持类型转化。所以CustomPerson中将status修改为如下方式,测试发现都可以正常进行参数转化:
    指定value值与excel表头相同:

 @ExcelProperty(value = "状态",converter = PersonalConvert.class)private String status;

    不指定value值:

@ExcelProperty(converter = PersonalConvert.class)private String status;

    另外补充一种全局设置自定义转化器的方式(ExcelReaderSheetBuilder支持注册自定义转化器):

 public static void main(String[] args) {ArrayList<CustomPerson> personArrayList = new ArrayList<CustomPerson>();EasyExcel.read("F:\\test.xls", CustomPerson.class, new PersonalListener<CustomPerson>(// 监听器中doAfterAllAnalysed执行此方法;所有读取完成之后处理逻辑personList->{for (CustomPerson person:personList){personArrayList.add(person);}})).sheet().registerConverter(new PersonalConvert()).doRead();  // 读取文件操作System.out.println(personArrayList);}

    进行全局设置后进行文件读取操作,easy excel实体类中status就不用使用@ExcelProperty注解了。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部