Java中将含10k独立JSON对象的文档转为数组及转义问题处理
将非SQL源数据库迁移至SQL数据库时,源输出的JSON文档由一系列独立JSON对象组成。使用Java的JSONObject时,它只能识别顶层对象,因此需要编写代码将这些独立对象转为数组。
当前做法是把JSON文档转成字符串,通过计数大括号定位每个对象并插入数组,代码如下:
for (int i = 0; i < doc.length(); i++) { char currentChar = doc.charAt(i); if (currentChar == '{') { Integer jsonStart = i; Integer openBrace = 1; Integer closeBrace = 0; while (openBrace > closeBrace) { i++; currentChar = doc.charAt(i); if (currentChar == '{') { openBrace++; } if (currentChar == '}') { closeBrace++; } } Integer jsonEnd = i; String currentString = doc.substring(jsonStart, jsonEnd + 1); JSONObject currentJSONObject = new JSONObject(currentString); returnJSONArray.put(currentJSONObject); } }
因为数据量较大,数据库被拆成多个含10k对象的文档。代码原本运行正常,但遇到值中包含大括号的文档时出错,于是添加代码识别引号包裹的值并忽略其中的大括号:
if (currentChar == '"') { i++; currentChar = mongoExport.charAt(i); while (!(currentChar == '"')) { i++; currentChar = mongoExport.charAt(i); } }
修改后解决了值含大括号的问题,但测试其他文档时出现**"String index out of range: 大数"**错误,根源是查找结束引号的while循环遇到了转义引号(\"),导致循环一直找不到结束引号,最终超出字符串长度。尝试检查引号前的转义字符但无效,且无法手动检查超长文档,请问如何处理这类字符串?是否有更简便的初始方法?
放弃手动解析JSON,使用成熟库:手动处理JSON的字符转义、嵌套结构等场景极易踩坑,转义引号、嵌套对象/数组、Unicode转义等情况都会让手写逻辑漏洞百出。直接用Jackson、Gson这类主流JSON解析库处理这种"JSON Lines"格式的文档,是最稳妥的方案。
用Jackson流式处理JSON Lines:
你的场景属于标准的JSON Lines格式(每个独立JSON对象占一行或连续排列),Jackson支持流式解析,无需将整个文档加载到内存,适合处理大体积文档。示例代码:import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class JsonLinesParser { public static void main(String[] args) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); File jsonFile = new File("your-input-file.json"); List<Object> jsonObjects = new ArrayList<>(); // 逐对象解析,内存占用低 try (var reader = objectMapper.readerFor(Object.class).readValues(jsonFile)) { while (reader.hasNext()) { Object jsonObj = reader.next(); jsonObjects.add(jsonObj); // 可直接在此处将对象写入SQL数据库,无需先存数组 } } } }若必须使用org.json的JSONObject:
可以先将文档拼接成合法的JSON数组字符串再解析,前提是文档中对象以换行分隔或无多余空格:import org.json.JSONArray; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class JsonToArray { public static void main(String[] args) throws IOException { StringBuilder sb = new StringBuilder("["); try (BufferedReader br = new BufferedReader(new FileReader("your-input-file.json"))) { String line; boolean isFirst = true; while ((line = br.readLine()) != null) { line = line.trim(); if (line.isEmpty()) continue; if (!isFirst) sb.append(","); sb.append(line); isFirst = false; } } sb.append("]"); JSONArray jsonArray = new JSONArray(sb.toString()); // 后续处理数组中的JSONObject } }若文档中对象是连续无换行的
{...}{...}格式,这种方式不适用,还是得用流式解析库。修复现有代码的转义引号判断逻辑:
若一定要维护现有代码,需正确判断引号是否被转义(连续偶数个反斜杠才是有效转义),修改后的引号处理逻辑:if (currentChar == '"') { i++; while (i < doc.length()) { currentChar = doc.charAt(i); if (currentChar == '"') { // 统计前导反斜杠数量,判断是否为转义引号 int backslashCount = 0; int j = i - 1; while (j >= 0 && doc.charAt(j) == '\\') { backslashCount++; j--; } // 偶数个反斜杠表示当前是结束引号 if (backslashCount % 2 == 0) break; } i++; } }但这种方式仍无法覆盖所有JSON边界场景,远不如使用现成库可靠。
内容的提问来源于stack exchange,提问作者ZenubisSpyke

