循环中将多个JSON字符串转为CSV的Java实现方案咨询
循环处理大JSON转CSV(单JSON 50MB+数百万行)的实现方案
针对你需要在循环中逐个处理大体积JSON、生成CSV且仅保留一行表头的场景,以下是两种高效可行的实现方案,均避免了加载巨型JSON数组的内存问题:
一、基础Java + Jackson(流式解析防OOM)
单个JSON达50MB时,直接将其转为对象会占用大量内存,因此采用Jackson的流式解析逐Token提取字段,配合BufferedWriter实现高效写入。
代码示例
import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class JsonToCsvBasic { private static final String CSV_PATH = "output.csv"; // 定义JSON字段与CSV表头的映射(支持大小写转换) private static final Map<String, String> FIELD_MAP = new HashMap<>(); static { FIELD_MAP.put("name", "name"); FIELD_MAP.put("Age", "age"); // 可扩展添加其他需要的字段 } public static void main(String[] args) { Iterable<MyBusinessObj> businessObjs = getBusinessObjects(); try (BufferedWriter writer = new BufferedWriter(new FileWriter(CSV_PATH, StandardCharsets.UTF_8)); JsonFactory jsonFactory = new JsonFactory()) { boolean isFirstRow = true; for (MyBusinessObj obj : businessObjs) { String json = convertBOToJson(obj); Map<String, String> fieldValues = new HashMap<>(); // 流式解析JSON,仅提取目标字段 try (JsonParser parser = jsonFactory.createParser(json)) { JsonToken token; String currentField = null; while ((token = parser.nextToken()) != null) { if (token == JsonToken.FIELD_NAME) { currentField = parser.getCurrentName(); } else if (token == JsonToken.VALUE_STRING && currentField != null && FIELD_MAP.containsKey(currentField)) { fieldValues.put(FIELD_MAP.get(currentField), parser.getValueAsString()); currentField = null; // 重置避免重复匹配 } } } // 写入表头(仅第一次循环执行) if (isFirstRow) { String header = String.join(",", FIELD_MAP.values()); writer.write(header); writer.newLine(); isFirstRow = false; } // 组装并写入数据行,自动处理引号转义 String row = String.join(",", FIELD_MAP.values().stream() .map(fieldValues::get) .map(val -> val == null ? "" : "\"" + val.replace("\"", "\"\"") + "\"") .toArray(String[]::new)); writer.write(row); writer.newLine(); } } catch (IOException e) { e.printStackTrace(); } } // 模拟业务对象获取方法 private static Iterable<MyBusinessObj> getBusinessObjects() { // 返回你的业务对象集合 return null; } // 模拟业务对象转JSON方法 private static String convertBOToJson(MyBusinessObj obj) { // 返回转换后的JSON字符串 return null; } }
二、OpenCSV + Jackson 实现
OpenCSV封装了CSV写入的细节(如分隔符、特殊字符转义),代码更简洁,搭配Jackson流式解析同样能避免内存溢出。
代码示例
import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.opencsv.CSVWriter; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class JsonToCsvOpenCsv { private static final String CSV_PATH = "output.csv"; private static final Map<String, String> FIELD_MAP = new HashMap<>(); static { FIELD_MAP.put("name", "name"); FIELD_MAP.put("Age", "age"); // 扩展其他字段映射 } public static void main(String[] args) { Iterable<MyBusinessObj> businessObjs = getBusinessObjects(); String[] csvHeaders = FIELD_MAP.values().toArray(new String[0]); try (CSVWriter writer = new CSVWriter(new FileWriter(CSV_PATH, StandardCharsets.UTF_8)); JsonFactory jsonFactory = new JsonFactory()) { boolean isFirstRow = true; for (MyBusinessObj obj : businessObjs) { String json = convertBOToJson(obj); Map<String, String> fieldValues = new HashMap<>(); // 流式解析JSON try (JsonParser parser = jsonFactory.createParser(json)) { JsonToken token; String currentField = null; while ((token = parser.nextToken()) != null) { if (token == JsonToken.FIELD_NAME) { currentField = parser.getCurrentName(); } else if (token == JsonToken.VALUE_STRING && currentField != null && FIELD_MAP.containsKey(currentField)) { fieldValues.put(FIELD_MAP.get(currentField), parser.getValueAsString()); currentField = null; } } } // 写入表头 if (isFirstRow) { writer.writeNext(csvHeaders); isFirstRow = false; } // 组装数据行并写入 String[] row = new String[csvHeaders.length]; for (int i = 0; i < csvHeaders.length; i++) { row[i] = fieldValues.getOrDefault(csvHeaders[i], ""); } writer.writeNext(row); } } catch (IOException e) { e.printStackTrace(); } } // 模拟方法 private static Iterable<MyBusinessObj> getBusinessObjects() { return null; } private static String convertBOToJson(MyBusinessObj obj) { return null; } }
核心优化点
- 流式解析:Jackson的
JsonParser逐Token解析JSON,无需加载全量50MB数据到内存,彻底避免OOM。 - 资源自动管理:使用
try-with-resources自动关闭流,防止资源泄漏。 - 性能缓冲:
BufferedWriter和OpenCSV的CSVWriter均内置缓冲,适配数百万行的高吞吐量写入。 - 字段映射:通过
FIELD_MAP统一管理JSON键与CSV表头的对应关系,便于后续扩展维护。
内容的提问来源于stack exchange,提问作者Nila
相关产品推荐
相关产品推荐

