C# 操作JSONL/NDJSON文件 C#如何读取和写入换行分隔的JSON文件

来源:这里教程网 时间:2026-02-21 17:42:59 作者:

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 已违反规范,得先统一转码再处理——没有银弹,只能前置清洗。

相关推荐

热文推荐