You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:55:29