1.场景问题说明
2.问题处理过程分析
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.8</version>
</dependency>
自定义格式转化器PersonalConvert
:
public class PersonalConvert implements Converter<String> {
// 指定转化参数的Java类型
@Override
public Class<?> supportJavaTypeKey() {
return String.class;
}
// 指定转化参数对应的单元格类型
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
// 单元格参数类型转化为Java类型处理逻辑
@Override
public 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;
@MobileValid
private 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 read:com.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支持参数类型转化就看一下不生效原因。
ModelBuildEventListener.java
中 buildUserModel
,读取每行数据并组装每行的结果模型。
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.java
中initOneColumnProperty
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);
}
DefaultAnalysisEventProcessor
中buildHead
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注解了。