导出UserProfile到Excel时报LinkedHashMap转String错误如何解决
问题根因
你使用ObjectMapper.convertValue()将UserProfile对象转为Map<String, Object>时,对象内的非基础类型不会自动序列化为字符串:
- 自定义类(如
Role、RoleGroup)会被转为LinkedHashMap类型 - 枚举、
Date、List类型也不会直接返回字符串值
这些非字符串类型直接强转为String就会抛出类型转换异常。
另外你现有代码还有一处逻辑错误:创建单元格后先写入了字段名key,后续又写入字段值value,会把字段名直接覆盖,这段冗余代码可以删掉。
解决方案
1. 优化工具类代码
首先将ObjectMapper提到静态变量位置(线程安全,避免重复创建消耗性能),然后修改单元格赋值逻辑,对所有非基础类型做通用序列化处理:
// 工具类内定义全局ObjectMapper private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); // 可选:定义不需要导出的字段列表 private static final Set<String> EXCLUDE_FIELDS = Set.of("roles", "roleIds", "transactionPin", "hasPin"); public static InputStream writeToExcel(Stream<UserProfile> data) throws Exception { try (XSSFWorkbook workbook = new XSSFWorkbook()) { XSSFSheet sheet = workbook.createSheet("transaction report"); AtomicInteger rowNum = new AtomicInteger(1); AtomicBoolean isHeaderSet = new AtomicBoolean(false); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); data.forEach(userProfile -> { if (!isHeaderSet.get()){ createHeader(userProfile, sheet); isHeaderSet.set(true); } XSSFRow row = sheet.createRow(rowNum.getAndIncrement()); Map<String, Object> mapObj = OBJECT_MAPPER.convertValue(userProfile, Map.class); AtomicInteger cellNum = new AtomicInteger(); mapObj.forEach((key, value) -> { // 跳过不需要导出的字段 if (EXCLUDE_FIELDS.contains(key)) { return; } XSSFCell cell = row.createCell(cellNum.getAndIncrement()); // 空值直接填空字符串 if (value == null) { cell.setCellValue(""); return; } // 分类型处理单元格值 if (value instanceof Integer) { cell.setCellValue((Integer) value); } else if (value instanceof BigDecimal) { cell.setCellValue(((BigDecimal) value).doubleValue()); } else if (value instanceof Long) { cell.setCellValue((Long) value); } else if (value instanceof Double) { cell.setCellValue((Double) value); } else if (value instanceof Boolean) { cell.setCellValue((Boolean) value); } else if (value instanceof Date) { cell.setCellValue(dateFormat.format((Date) value)); } else { // 其他类型统一序列化为JSON字符串,序列化失败用toString兜底 try { cell.setCellValue(OBJECT_MAPPER.writeValueAsString(value)); } catch (JsonProcessingException e) { cell.setCellValue(value.toString()); } } }); }); // 原有后续输出流逻辑保持不变 // ... } }
2. 可选优化
如果不需要导出roles这类复合字段,直接在EXCLUDE_FIELDS里加对应的字段名即可,不需要做额外序列化处理。
内容的提问来源于stack exchange,提问作者Curious
相关产品推荐
相关产品推荐

