用 streamreader 逐行读取 jsonl 文件最稳,因其每行是独立 json 对象,须逐行解析而非整体反序列化;写入时禁用 writeindented 并用 writeline 确保单行格式;处理大文件需异步读取并显式设置缓冲区与编码以规避 bom 和超长行问题。

用 StreamReader 逐行读取 JSONL 文件最稳
JSONL(也叫 NDJSON)本质就是每行一个合法 JSON 对象,不能当完整 JSON 数组去解析。直接用 JsonSerializer.Deserialize<list>>()</list> 会报 JsonException: The input does not contain any JSON tokens 或中途崩溃——因为第一行之后的换行符会让反序列化器误判流已结束。
正确做法是按行读,每行单独解析:
- 用
StreamReader打开文件,调用ReadLine()逐行获取字符串 - 对每一行非空字符串,传给
JsonSerializer.Deserialize<t>()</t>,别用DeserializeAsync——它不适用于碎片化输入 - 遇到某行解析失败(比如字段缺失、类型错),建议记录该行号和原始内容,而不是直接抛异常中断整个读取
示例关键片段:
using var reader = new StreamReader("data.jsonl");
string? line;
int lineNumber = 0;
while ((line = await reader.ReadLineAsync()) != null)
{
lineNumber++;
if (string.IsNullOrWhiteSpace(line)) continue;
try
{
var item = JsonSerializer.Deserialize<MyRecord>(line);
// 处理 item
}
catch (JsonException ex)
{
Console.WriteLine($"Line {lineNumber} parse failed: {ex.Message}");
}
}
写入 JSONL 必须手动控制换行,不能依赖 JsonSerializerOptions.WriteIndented
开启 WriteIndented = true 会让每个对象带缩进和多行格式,彻底破坏 JSONL 格式(要求每行严格一个对象,且无换行)。结果是:后续工具(如 jq、pandas.read_json(lines=True))全读不出来,或只读到第一行。
写入时要确保:
- 每个对象序列化后是单行纯文本,结尾加
\n(不是\r\n,除非明确目标环境只认 Windows 换行) - 用
StreamWriter,并显式调用WriteLine()——它自动补\n,且不加空格或缩进 - 避免用
Console.WriteLine()或拼接字符串写入,容易混入不可见字符
示例:
using var writer = new StreamWriter("output.jsonl");
foreach (var item in data)
{
string json = JsonSerializer.Serialize(item, new JsonSerializerOptions
{
WriteIndented = false, // 必须关掉
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
await writer.WriteLineAsync(json); // WriteLine 自动加 \n
}
处理大文件时,StreamReader.ReadLineAsync 比同步版更安全
读取几百 MB 甚至 GB 级 JSONL 文件时,同步 ReadLine() 会阻塞线程,拖慢整个应用响应;而 ReadLineAsync() 配合 ConfigureAwait(false) 能释放线程资源,尤其在 ASP.NET Core 后端或长时间运行服务中很关键。
但要注意:
- 必须在
async方法里用,别用.Result或.Wait()强制同步——可能死锁 -
StreamReader默认缓冲区是 1024 字节,如果某一行超长(比如嵌入了 base64 图片),可能触发ArgumentException: Buffer too small;此时应显式传入更大缓冲区,如new StreamReader(stream, Encoding.UTF8, true, 65536) - 不要用
File.ReadLines()——它底层仍是同步读,且无法控制缓冲区大小
别忽略 BOM 和编码问题,UTF-8 with BOM 会让首行解析失败
Windows 记事本保存的 UTF-8 文件常带 BOM(EF BB BF),导致第一行开头出现非法字符,JsonSerializer.Deserialize() 直接报 InvalidDataException: Invalid prefix 或类似错误,但错误信息不提示 BOM。
稳妥做法:
- 读取前用
new StreamReader(stream, Encoding.UTF8, true),第三个参数detectEncodingFromByteOrderMarks = true会自动跳过 BOM - 写入时用
new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))确保不写 BOM - 如果必须支持带 BOM 的文件,可在读取后对首行做
line.TrimStart('\uFEFF'),但不如让StreamReader自动处理干净
真正麻烦的是混合编码文件(比如部分行是 GBK),这种 JSONL 已违反规范,得先统一转码再处理——没有银弹,只能前置清洗。










