JavaScript 处理 JSONL:读取、写入与解析
使用 JavaScript 处理 JSONL(JSON Lines)文件的完整指南。学习如何在 Node.js 和浏览器中使用内置 API 和常用库来读取、写入、解析和流式处理 JSONL 数据。
最后更新:2026年2月
为什么选择 JavaScript 处理 JSONL?
JavaScript 天然适合处理 JSONL 文件。JSON 本身就源自 JavaScript,因此该语言通过内置的 JSON.parse() 和 JSON.stringify() 方法提供了一流的解析支持。无论您是在构建 Node.js 数据管道、在服务器上处理日志文件,还是让用户在浏览器中上传 JSONL,JavaScript 都能为每个环节提供合适的工具。
JSONL(JSON Lines)每行存储一个 JSON 对象,非常适合流式处理、追加式日志记录和逐行处理大型数据集。Node.js 的流(stream)与这种格式完美契合,让您可以读取和转换数百万条记录而不会耗尽内存。在客户端,浏览器的 FileReader 和 Streams API 可以完全在用户设备上处理 JSONL。本指南将介绍如何在 Node.js 和浏览器中读取 JSONL、写入 JSONL 文件、构建流式转换管道,以及如何选择适合项目的 npm 库。
在 Node.js 中读取 JSONL
Node.js 提供了 readline 模块来高效地逐行读取文件。结合 fs.createReadStream,这是在服务器上处理 JSONL 文件的推荐方式,因为它以流式传输数据,而不是将整个文件加载到内存中。
使用 fs.createReadStream 管道到 readline.createInterface 来逐行读取 JSONL 文件。这种方式无论文件大小如何,都只使用极少的内存。
import { createReadStream } from 'node:fs';import { createInterface } from 'node:readline';async function readJsonl(filePath) {const records = [];const rl = createInterface({input: createReadStream(filePath, 'utf-8'),crlfDelay: Infinity,});for await (const line of rl) {const trimmed = line.trim();if (trimmed) {records.push(JSON.parse(trimmed));}}return records;}// Usageconst data = await readJsonl('data.jsonl');console.log(`Loaded ${data.length} records`);console.log(data[0]);
对于可以轻松放入内存的小型 JSONL 文件,您可以一次性读取整个文件并按换行符分割。这种方式用简洁性换取了流式处理的效率。
import { readFileSync } from 'node:fs';const records = readFileSync('data.jsonl', 'utf-8').split('\n').filter(line => line.trim()).map(line => JSON.parse(line));console.log(`Loaded ${records.length} records`);
在浏览器中读取 JSONL
在浏览器中,用户可以通过文件输入框上传 JSONL 文件。您可以使用 FileReader API 或现代的 Streams API 来读取文件,所有操作都无需将数据发送到服务器。
使用 FileReader 读取用户选择的文件,然后将文本内容按行分割并逐行解析。所有数据都保留在客户端,确保隐私安全。
function parseJsonlFile(file) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => {const text = reader.result;const records = text.split('\n').filter(line => line.trim()).map(line => JSON.parse(line));resolve(records);};reader.onerror = () => reject(reader.error);reader.readAsText(file, 'utf-8');});}// Usage with an <input type="file"> elementconst input = document.querySelector('input[type="file"]');input.addEventListener('change', async (e) => {const file = e.target.files[0];const records = await parseJsonlFile(file);console.log(`Parsed ${records.length} records`);});
对于浏览器中的大文件,使用 Streams API 配合 TextDecoderStream 来逐块处理文件,避免一次性将整个文件加载到内存中。
async function* streamJsonl(file) {const stream = file.stream().pipeThrough(new TextDecoderStream());const reader = stream.getReader();let buffer = '';while (true) {const { done, value } = await reader.read();if (done) break;buffer += value;const lines = buffer.split('\n');buffer = lines.pop(); // Keep incomplete last linefor (const line of lines) {const trimmed = line.trim();if (trimmed) yield JSON.parse(trimmed);}}// Handle remaining bufferif (buffer.trim()) {yield JSON.parse(buffer.trim());}}// Usagefor await (const record of streamJsonl(file)) {console.log(record);}
在 Node.js 中写入 JSONL
在 Node.js 中写入 JSONL 文件很简单:将每个 JavaScript 对象序列化为 JSON 字符串并追加换行符。对于大型数据集,使用写入流(write stream)而不是在内存中构建完整字符串可以获得更好的性能。
使用 fs.writeFileSync 或 fs.createWriteStream 将记录写入 JSONL 文件。每条记录是一行有效的 JSON,后面跟一个换行符。
import { writeFileSync } from 'node:fs';const records = [{ id: 1, name: 'Alice', role: 'engineer' },{ id: 2, name: 'Bob', role: 'designer' },{ id: 3, name: 'Charlie', role: 'manager' },];const jsonl = records.map(record => JSON.stringify(record)).join('\n') + '\n';writeFileSync('output.jsonl', jsonl, 'utf-8');console.log(`Wrote ${records.length} records`);
写入数百万条记录时,使用可写流来避免在内存中构建完整的输出字符串。流会自动处理背压(backpressure),当操作系统缓冲区满时暂停写入。
import { createWriteStream } from 'node:fs';async function writeJsonl(filePath, records) {const stream = createWriteStream(filePath, 'utf-8');for (const record of records) {const line = JSON.stringify(record) + '\n';// Respect backpressureif (!stream.write(line)) {await new Promise(resolve => stream.once('drain', resolve));}}stream.end();await new Promise(resolve => stream.on('finish', resolve));console.log('Write complete');}// Usageconst data = Array.from({ length: 100000 }, (_, i) => ({id: i + 1,value: Math.random(),timestamp: new Date().toISOString(),}));await writeJsonl('large_output.jsonl', data);
JSONL 管道的 Transform Stream
Node.js 的 Transform stream 让您可以构建可组合的数据管道:读取 JSONL、处理每条记录、然后写入结果。这种模式非常适合 ETL 任务、日志处理和数据迁移。
import { createReadStream, createWriteStream } from 'node:fs';import { createInterface } from 'node:readline';import { Transform } from 'node:stream';import { pipeline } from 'node:stream/promises';// Transform: parse JSONL line into object, process, stringify backconst transformJsonl = new Transform({objectMode: true,transform(chunk, encoding, callback) {const line = chunk.toString().trim();if (!line) return callback();try {const record = JSON.parse(line);// Add your transformation hererecord.processed = true;record.processedAt = new Date().toISOString();callback(null, JSON.stringify(record) + '\n');} catch (err) {callback(err);}},});// Build the pipelineconst rl = createInterface({input: createReadStream('input.jsonl', 'utf-8'),crlfDelay: Infinity,});const output = createWriteStream('output.jsonl', 'utf-8');for await (const line of rl) {transformJsonl.write(line);}transformJsonl.end();await pipeline(transformJsonl, output);console.log('Pipeline complete');
这个管道逐行读取 JSONL 文件,对每条记录应用转换(添加 processed 和 processedAt 字段),然后将结果写入新文件。Transform stream 自动处理背压,因此即使对于非常大的文件,内存使用也保持恒定。您可以链式连接多个 transform 来构建复杂的 ETL 工作流。
JavaScript JSONL 库
虽然内置的 JSON.parse 可以处理大多数情况,但有一些 npm 包为 JSONL 特定的工作流提供了便利工具,如流式处理、验证和批量处理。
JSON.parse(内置)
内置内置的 JSON.parse() 和 JSON.stringify() 在 V8 引擎中经过高度优化,对于大多数 JSONL 用例已经足够。结合 readline 即可实现流式处理。无需任何依赖,对于数百 MB 的文件性能表现优秀。
ndjson
流行ndjson 是一个流行的 npm 包,提供与 Node.js 流兼容的流式 JSONL(换行分隔 JSON)解析器和序列化器。它能优雅地处理解析错误,并与现有的流管道良好集成。非常适合快速原型开发。
jsonl-parse-stringify
简洁jsonl-parse-stringify 是一个轻量级、零依赖的库,提供简单的 parse() 和 stringify() 方法来处理 JSONL 数据。它能处理尾部换行和空行等边界情况。适合需要简洁同步 API 而无需流设置的场景。
试试我们的免费 JSONL 工具
不想写代码?使用我们的免费在线工具直接在浏览器中查看、转换和格式化 JSONL 文件。所有处理均在本地完成,您的数据始终保持私密。