使用Jackson反序列化含整行双引号包裹的CSV文件
使用jackson-dataformat-csv处理异常格式的CSV行
问题场景
给定如下格式的CSV文件:
firstName,lastName,address "John,Doe,"Imaginary St. 42, 1234 Hometown"" Jane,Doe,"Castle St. 1, 5678 Littletown"
需要将其反序列化为如下Java类的对象:
public class PersonRecord { private String firstName; private String lastName; private String address; // 可选:添加getter/setter,或配置Jackson直接访问字段(见下文) }
核心挑战:部分行被整体用双引号包裹(需移除这层外层双引号),但address字段本身的值允许包含双引号,不能误删这些内部引号。
无需临时文件的最优方案
通过自定义Reader在流读取阶段预处理每行数据,直接修正格式后交给Jackson解析,全程无需生成临时文件。
1. 实现自定义行处理Reader
继承FilterReader,重写readLine方法,针对性移除行首尾的外层双引号:
import java.io.FilterReader; import java.io.IOException; import java.io.Reader; public class LineCleaningReader extends FilterReader { protected LineCleaningReader(Reader in) { super(in); } @Override public String readLine() throws IOException { String line = super.readLine(); if (line == null) { return null; } // 已知第一列不会被引号包裹,因此首尾均为双引号时才处理 if (line.startsWith("\"") && line.endsWith("\"")) { return line.substring(1, line.length() - 1); } return line; } }
2. 配置Jackson并完成解析
用自定义Reader包装原始输入流,结合CsvMapper完成反序列化:
import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.databind.introspect.JsonAutoDetect; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.FileReader; import java.util.ArrayList; import java.util.List; public class CsvParserDemo { public static void main(String[] args) throws IOException { CsvMapper mapper = new CsvMapper(); // 可选配置:无需getter/setter,让Jackson直接访问类字段 mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() .withFieldVisibility(JsonAutoDetect.Visibility.ANY) .withGetterVisibility(JsonAutoDetect.Visibility.NONE) .withSetterVisibility(JsonAutoDetect.Visibility.NONE) .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); // 构建CSV schema:用首行作为列名映射到PersonRecord的字段 CsvSchema schema = mapper.schemaFor(PersonRecord.class).withHeader(); // 用自定义Reader包装原始文件输入流 try (LineCleaningReader reader = new LineCleaningReader(new FileReader("your-file.csv"))) { MappingIterator<PersonRecord> iterator = mapper.readerFor(PersonRecord.class) .with(schema) .readValues(reader); List<PersonRecord> persons = new ArrayList<>(); while (iterator.hasNext()) { persons.add(iterator.next()); } // 验证解析结果 persons.forEach(p -> { System.out.println("First Name: " + p.firstName); System.out.println("Last Name: " + p.lastName); System.out.println("Address: " + p.address); System.out.println("---"); }); } } }
方案说明
- 流预处理:自定义Reader在数据读取阶段直接修正格式,内存占用低,效率高于临时文件方案。
- 安全处理:基于“第一列不会被引号包裹”的前提,仅移除首尾均为双引号的行的外层引号,避免误操作正常行。
- 兼容字段内引号:Jackson的CSV解析器会自动处理
address字段内部的双引号(比如示例中"Imaginary St. 42, 1234 Hometown"),无需额外处理。
内容的提问来源于stack exchange,提问作者Thomas W.
相关产品推荐
相关产品推荐

