Excel导出报Can not find ‘Converter‘ support class Map.
在使用alibaba
的Excel
导出功能时报了Caused by: com.alibaba.excel.exception.ExcelWriteDataConvertException: Can not find 'Converter' support class Map.
这个错误是 EasyExcel 无法序列化 Map 类型 导致的。问题出现在导出的实体类中包含 Map 类型的字段。
public class Merchant {private String merchantName;private Map<String, Object> extendFields;}
解决方案
方案1:在字段上添加忽略注解
public class Merchant {private String merchantName;@ExcelIgnore // 添加这个注解,导出时忽略该字段private Map<String, Object> extendFields;
}
方案2:自定义 Converter
创建 Map 类型的转换器:
public class MapConverter implements Converter<Map<String, Object>> {@Overridepublic Class<?> supportJavaTypeKey() {return Map.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Map<String, Object> convertToJavaData(ReadConverterContext<?> context) {return null; // 导入时不需要}@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<Map<String, Object>> context) {// 将 Map 转换为字符串格式导出Map<String, Object> map = context.getValue();if (map == null || map.isEmpty()) {return new WriteCellData<>("");}try {String jsonString = new ObjectMapper().writeValueAsString(map);return new WriteCellData<>(jsonString);} catch (Exception e) {return new WriteCellData<>(map.toString());}}
}
然后在字段上使用:
public class Merchant {@ExcelProperty(converter = MapConverter.class)private Map<String, Object> params;
}
方案3:导出时忽略字段(推荐)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {/*** 导出excel** @param list 导出数据集合* @param sheetName 工作表的名称* @param clazz 实体类* @param response 响应体* @param excludeColumns 不导出的列名(关键)*/public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response, List<String> excludeColumns) {try {resetResponse(sheetName, response);ServletOutputStream os = response.getOutputStream();exportExcel(list, sheetName, clazz, false, os, excludeColumns);} catch (IOException e) {throw new RuntimeException("导出Excel异常");}}/*** 导出excel** @param list 导出数据集合* @param sheetName 工作表的名称* @param clazz 实体类* @param merge 是否合并单元格* @param os 输出流* @param excludeColumns 不导出的列名*/public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os, List<String> excludeColumns) {ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz).autoCloseStream(false)// 自动适配.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())// 大数值自动转换 防止失真 可注册多个.registerConverter(new ExcelBigNumberConvert()).excludeColumnFieldNames(excludeColumns).sheet(sheetName);if (merge) {// 合并处理器builder.registerWriteHandler(new CellMergeStrategy(list, true));}builder.doWrite(list);}
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;import java.math.BigDecimal;/*** 大数值转换* Excel 数值长度位15位 大于15位的数值转换位字符串** @author Lion Li*/
@Slf4j
public class ExcelBigNumberConvert implements Converter<Long> {@Overridepublic Class<Long> supportJavaTypeKey() {return Long.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {return Convert.toLong(cellData.getData());}@Overridepublic WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {if (ObjectUtil.isNotNull(object)) {String str = Convert.toStr(object);if (str.length() > 15) {return new WriteCellData<>(str);}}WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));cellData.setType(CellDataTypeEnum.NUMBER);return cellData;}}
问题
问题1:当要导出的list中含有某个字段且有值,但是该字段并没有使用@ExcelProperty注解标注,这个字段会被导出吗
不会导出。EasyExcel 默认只会导出使用了 @ExcelProperty 注解的字段。
EasyExcel 的默认行为
-
默认不导出任何字段
如果没有使用 @ExcelProperty 注解,字段不会被导出:public class Merchant {private String merchantName; // 不会导出// 这个字段也不会导出,即使有值private String unannotatedField = "我有值但不会被导出"; }
-
只有明确标注的字段才会导出
public class Merchant {@ExcelProperty("商户名称")private String merchantName; // 会导出,列名为"商户名称"private String address; // 不会导出 }
特殊情况
-
如果所有字段都没有注解
如果类中没有任何字段使用 @ExcelProperty,EasyExcel 会尝试按照字段声明的顺序导出所有字段,并使用字段名作为列名:public class Merchant {private String merchantName; // 可能导出为 "merchantName"private String status; // 可能导出为 "status" private String phone; // 可能导出为 "phone" }
但这种行为不可靠,不同版本可能表现不同。
-
使用
@ExcelIgnoreUnannotated
控制
可以使用@ExcelIgnoreUnannotated
注解明确控制:@ExcelIgnoreUnannotated // 明确忽略未注解的字段 public class Merchant {@ExcelProperty("商户名称")private String merchantName; // 会导出private String status; // 不会导出(被忽略) }
最佳实践建议
明确标注所有需要导出的字段
使用 @ExcelIgnore
明确忽略不需要的字段
避免依赖默认行为,不同版本的 EasyExcel 可能表现不同
public class MerchantExportVO {@ExcelProperty("商户名称")private String merchantName;@ExcelIgnore // 明确忽略private Map<String, Object> params;@ExcelIgnore // 明确忽略private String internalCode;
}
总结:没有 @ExcelProperty
注解的字段不会被导出,无论它是否有值。这是 EasyExcel 的安全机制,避免意外泄露数据。
问题二:那为什么Caused by: com.alibaba.excel.exception.ExcelWriteDataConvertException: Can not find ‘Converter’ support class Map.但这个private Map<String, Object> params;字段我并没有使用@ExcelProperty
这个问题很关键!即使字段没有使用 @ExcelProperty,EasyExcel 仍然会尝试处理这个字段,这就是报错的原因。
问题根源
EasyExcel 在写入时,会扫描类的所有字段,包括没有 @ExcelProperty
注解的字段。当它发现 Map 类型的字段时,会尝试为其寻找合适的 Converter,但找不到就报错了。