
本文介绍一种实用方法,解决 csv 中某列值本身为 json 字符串(含逗号、花括号等特殊字符)导致解析失败的问题,通过预处理+自定义引号字符的方式,借助 jackson 安全地完成 csv 到嵌套 json 的转换。
在实际数据集成场景中,常遇到 CSV 文件某列(如 header3)直接存储了 JSON 格式的字符串(例如 {"name":"John","age":30,"car":null})。标准 CSV 解析器会将其中的逗号、花括号或引号误判为分隔符或结构标记,导致解析错误或字段截断。虽然规范要求此类值应被双引号包裹(如 "{"name":"John"}"),但若上游系统无法修正输出(即 CSV 本身“不合规”),我们就需在解析前进行智能预处理。
核心思路是:临时替换 JSON 边界符号,使其能被 CSV 解析器识别为受保护的字段内容。由于原始 CSV 未使用引号,我们选择用 | 作为临时引号字符,并仅对最外层 { 和 } 进行配对替换(避免破坏 JSON 内部结构):
- 将每行中第一个 { 替换为 |{
- 将每行中**最后一个 } 替换为 }|
这样,{"name":"John","age":30,"car":null} 变为 |{"name":"John","age":30,"car":null}|,CSV 解析器即可将其整体视为一个带引号的字段值。
以下是完整可运行示例(基于 Jackson 2.15+):
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializationFeature;
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.List;
import java.util.stream.Collectors;
public class CsvApp {
public static void main(String[] args) throws Exception {
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
List lines = Files.readAllLines(csvFile.toPath());
// 预处理:仅替换每行首尾的 { 和 },避免影响内部 JSON
String processedCsv = lines.stream()
.map(line -> {
int firstBrace = line.indexOf('{');
int lastBrace = line.lastIndexOf('}');
if (firstBrace != -1 && lastBrace != -1 && firstBrace < lastBrace) {
return line.substring(0, firstBrace) + "|{"
+ line.substring(firstBrace + 1, lastBrace)
+ "}|"
+ line.substring(lastBrace + 1);
}
return line; // 无 JSON 结构则保持原样
})
.collect(Collectors.joining(System.lineSeparator()));
// 构建 CSV 解析器:指定 | 为引号字符,启用表头
CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = CsvSchema.builder()
.setQuoteChar('|')
.setUseHeader(true)
.build();
// 解析为 JsonNode(自动映射为 Map-like 结构)
JsonNode csvContent = csvMapper.readerFor(JsonNode.class)
.with(schema)
.readValue(processedCsv);
// 输出格式化 JSON(含缩进)
JsonMapper jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();
jsonMapper.writeValue(System.out, csvContent);
}
} ✅ 关键注意事项:
- 该方案适用于 JSON 值严格位于单个字段内且不含跨行内容的场景;
- 若 CSV 中存在多个 JSON 字段,需扩展预处理逻辑(例如按列索引定位并替换);
- 生产环境建议增加异常校验(如 firstBrace/lastBrace 匹配性检查、JSON 有效性验证);
- 更健壮的替代方案是改用支持自由格式字段的解析器(如 OpenCSV + 手动 split + JSON 解析),但 Jackson 方案更轻量、统一。
最终输出符合预期——header3 的值被保留为转义后的 JSON 字符串(即 "\"{\\\"name\\\":\\\"John\\\",\\\"age\\\":30,\\\"car\\\":null}\""),可在后续步骤中按需调用 JsonMapper.readTree() 二次解析为嵌套对象。










