本文介绍一种绕过 csv 格式缺陷的实用方案:当 csv 中某列值本身是未加引号的 json 字符串(如 {"name":"john","age":30})时,通过预处理替换边界符号并自定义引号字符,使 jackson 能安全解析嵌套 json 为字符串字段,最终输出符合预期的 json 结构。
CSV 规范明确要求:若字段值包含分隔符(如逗号)、换行符或双引号,必须用引号(默认为 ")包裹。但现实中,部分系统导出的 CSV 会直接将 JSON 字符串作为纯文本写入某列,且未加引号——这会导致标准 CSV 解析器误判字段边界,造成解析失败或数据错位。
例如以下非法 CSV:
header1,header2,header3
value1,value2,{"name":"John","age":30,"car":null}第三列的 JSON 含逗号,却无引号包裹,CsvMapper 默认会将其截断为 {"name":"John" 和 "age":30,"car":null} 两列,破坏结构。
由于无法修改上游生成逻辑,我们采用轻量级文本预处理 + 自定义引号字符的方式:
⚠️ 注意:该方法适用于 JSON 字段不嵌套在更复杂结构中(如 CSV 行内含多个 JSON 列),且 JSON 本身不含 | 字符的场景。若存在 |,需改用更安全的分隔符(如 \u0001)或正则精准匹配首尾大括号。
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.File; import java.nio.file.Files; import java.util.stream.Collectors; public class CsvToJsonConverter { public static void main(String[] args) throws Exception { File csvFile = new File("./resource/test.csv").getAbsoluteFile(); String csv = Files.readAllLines(csvFile.toPath()) .stream() .collect(Collectors.joining(System.lineSeparator())); // ✅ 关键预处理:仅包裹最外层 JSON 的 {},避免破坏内部结构 csv = csv.replaceFirst("(?<=,|^)\\{", "|{") .replaceAll("}\\s*(?=,|$)", "}|"); CsvMapper csvMapper = CsvMapper.builder().build(); CsvSchema schema = CsvSchema.builder() .setQuoteChar('|') .setUseHeader(true) .build(); JsonNode jsonNode = csvMapper.readerFor(JsonNode.class) .with(schema) .readValue(csv); // 输出格式化 JSON(含缩进) JsonMapper jsonMapper = JsonMapper.builder() .enable(JsonMapper.Feature.INDENT_OUTPUT) .build(); jsonMapper.writeValue(System.out, jsonNode); } }
{
"header1" : "value1",
"header2" : "value2",
"header3" : "{\"name\":\"John\",\"age\":30,\"car\":null}"
}可见 header3 的值被正确保留为转义后的 JSON 字符串,完全符合需求。