使用Apache POI写入Excel时优化JSON空值多IF判断的方案咨询
优化数组型JSON响应空值判断与Excel写入性能方案
核心问题分析
之前的方案速度慢,本质是重复调用JSON路径解析接口(response.path()),每条路径都要重新遍历JSON结构,400条路径等于重复遍历400次;合并判断出现空指针异常,是因为只检查了数组节点非空,没考虑数组长度不足索引值或数组元素本身为null的情况。
优化方案
1. 批量预解析JSON,缓存有效节点
一次性解析整个JSON结构,遍历所有data.testN数组,提前筛选出非空元素并缓存,彻底避免重复路径解析的开销。
示例代码(以Jackson JSON库为例):
// 1. 一次性将响应解析为JsonNode ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(response.getInputStream()); JsonNode dataNode = rootNode.get("data"); if (dataNode == null || dataNode.isNull()) { return; } // 2. 用Map缓存所有非空路径与对应值,初始容量设为路径数减少扩容开销 Map<String, Object> validData = new HashMap<>(400); // 遍历所有test节点(假设命名为test1到test400,可根据实际动态获取节点名) for (int testIdx = 1; testIdx <= 400; testIdx++) { String testKey = "test" + testIdx; JsonNode arrayNode = dataNode.get(testKey); // 跳过非数组、空数组的情况 if (arrayNode == null || arrayNode.isNull() || !arrayNode.isArray() || arrayNode.size() == 0) { continue; } // 遍历数组元素,只保留非空值 for (int elemIdx = 0; elemIdx < arrayNode.size(); elemIdx++) { JsonNode elemNode = arrayNode.get(elemIdx); if (elemNode != null && !elemNode.isNull()) { String path = String.format("data.%s[%d]", testKey, elemIdx); // 按节点类型转换为对应Java类型 Object value = switch (elemNode.getNodeType()) { case STRING -> elemNode.asText(); case NUMBER -> elemNode.asDouble(); case BOOLEAN -> elemNode.asBoolean(); default -> elemNode.toString(); }; validData.put(path, value); } } }
2. 安全的空值判断逻辑(避免NPE)
如果必须保留逐路径判断的方式,要补全三层判断逻辑:
// 完整判断:数组存在 → 索引不越界 → 元素非空 JsonNode test1Node = dataNode.get("test1"); if (test1Node != null && test1Node.isArray() && test1Node.size() > 0) { JsonNode elem0 = test1Node.get(0); if (elem0 != null && !elem0.isNull()) { // 写入Excel逻辑 } }
也可以用JSON库的内置方法简化判断:
// Jackson的has() + get() 链式安全判断 if (dataNode.has("test1") && dataNode.get("test1").isArray() && dataNode.get("test1").size() > 0) { JsonNode elem = dataNode.get("test1").get(0); if (!elem.isNull()) { // 写入逻辑 } }
3. 优化Excel写入性能
SXSSF没改善可能是写入方式不对,调整为批量写入提升效率:
- 提前准备好一行的所有单元格值,一次性创建行和单元格,避免频繁操作Sheet
- 关闭POI的自动公式计算,减少不必要的开销
- 设置SXSSF的内存保留行数,控制内存占用
示例代码:
SXSSFWorkbook workbook = new SXSSFWorkbook(200); // 内存保留200行,超过则写入临时文件 SXSSFSheet sheet = workbook.createSheet("Test Results"); sheet.setForceFormulaRecalculation(false); // 关闭自动公式计算 // 假设路径列表已提前定义 List<String> pathList = Arrays.asList("data.test1[0]", "data.test1[1]", ...); // 批量写入一行(多行同理) SXSSFRow row = sheet.createRow(0); int cellIdx = 0; for (String path : pathList) { // 空值用空字符串填充 Object value = validData.getOrDefault(path, ""); SXSSFCell cell = row.createCell(cellIdx++); // 根据值类型设置单元格类型 if (value instanceof String) { cell.setCellValue((String) value); } else if (value instanceof Double) { cell.setCellValue((Double) value); } else if (value instanceof Boolean) { cell.setCellValue((Boolean) value); } } // 写入文件并清理资源 try (FileOutputStream fos = new FileOutputStream("test_result.xlsx")) { workbook.write(fos); } finally { workbook.dispose(); // 清理SXSSF临时文件 }
4. 并行解析(可选)
如果JSON结构特别庞大,可以把400条testN节点分成多个批次,用线程池并行解析节点、缓存数据,最后单线程写入Excel(Excel写入线程不安全,必须单线程):
ExecutorService executor = Executors.newFixedThreadPool(4); // 根据CPU核心数设置线程数 List<Future<Void>> futures = new ArrayList<>(); // 注意:validData要使用线程安全的Map,比如ConcurrentHashMap Map<String, Object> validData = new ConcurrentHashMap<>(400); for (int i = 1; i <= 400; i++) { int testIdx = i; futures.add(executor.submit(() -> { String testKey = "test" + testIdx; JsonNode arrayNode = dataNode.get(testKey); // 解析逻辑同批量预解析 if (arrayNode != null && arrayNode.isArray() && arrayNode.size() > 0) { for (int elemIdx = 0; elemIdx < arrayNode.size(); elemIdx++) { JsonNode elemNode = arrayNode.get(elemIdx); if (elemNode != null && !elemNode.isNull()) { String path = String.format("data.%s[%d]", testKey, elemIdx); Object value = elemNode.asText(); // 根据实际类型调整 validData.put(path, value); } } } return null; })); } // 等待所有任务完成 for (Future<Void> future : futures) { future.get(); } executor.shutdown();
总结
- 核心优化点是减少JSON路径解析次数,通过一次性遍历缓存所有有效数据,避免重复开销
- 空值判断要覆盖「数组存在、索引合法、元素非空」三层场景,彻底避免空指针异常
- Excel写入采用批量操作,配合SXSSF的内存优化参数,减少IO操作次数
内容的提问来源于stack exchange,提问作者Flenters
相关产品推荐
相关产品推荐

