NDJSON 完整指南:換行分隔 JSON 詳解
換行分隔 JSON(NDJSON)完整指南。了解規格、MIME 類型、如何在 Python、Node.js 和命令列中讀寫 NDJSON、在串流 HTTP API 中的應用,以及它與 JSONL 的關係。
最後更新:2026 年 2 月
什麼是 NDJSON?
NDJSON 全稱為 Newline Delimited JSON(換行分隔 JSON)。它是一種基於文字的資料格式,其中每一行包含恰好一個有效的 JSON 值,以換行符(\n)分隔。這種格式專為串流和處理大型資料集而設計,無需將整個檔案載入記憶體。與將所有內容包裝在單一陣列或物件中的標準 JSON 檔案不同,NDJSON 讓您可以一次讀取、寫入和處理一筆記錄。
NDJSON 規格託管在 github.com/ndjson/ndjson-spec。它的建立是為了將日誌傳送、資料管線和 HTTP 串流 API 中已經普遍使用的模式正式化。每一行都是自包含的:如果某一行包含無效的 JSON,其他行仍然可以成功解析。這使得 NDJSON 具有容錯性,非常適合僅追加的工作流程,例如應用程式日誌、事件串流和增量資料匯出。
{"id":1,"event":"page_view","url":"/home"}{"id":2,"event":"click","url":"/pricing"}{"id":3,"event":"signup","url":"/register"}
NDJSON 規格
github.com/ndjson/ndjson-spec 上的官方 NDJSON 規格刻意保持極簡。它定義了一種在單一文字串流中序列化多個 JSON 值的簡單慣例。核心規則很直觀:
規格明確避免在格式中添加標頭、元資料或 Schema 資訊。這使得 NDJSON 盡可能簡單,並與標準 Unix 文字處理工具相容。每一行允許任何有效的 JSON 值,但在實際應用中,大多數 NDJSON 檔案包含具有一致鍵集的 JSON 物件。
- 每一行必須包含恰好一個有效的 JSON 值(物件、陣列、字串、數字、布林值或 null)。
- 行以換行符 '\n'(U+000A)分隔。回車符 '\r'(U+000D)可以出現在 '\n' 之前,但不是必需的。
- 最後一個 JSON 值之後允許有尾隨換行符,但不是必需的。
- 每個 JSON 值本身內部不得包含未轉義的換行符。
由於規則極簡,NDJSON 可以從任何具有 JSON 序列化器的程式語言中輕鬆生成。只需序列化每筆記錄、附加換行符,然後寫入輸出。不需要結束括號、尾隨逗號或外層陣列結構。
NDJSON vs JSON vs JSONL
NDJSON、JSON 和 JSONL 各有不同的結構。標準 JSON 編碼單一值(通常是陣列或物件)。JSONL 和 NDJSON 都是每行儲存一個 JSON 值,兩者在功能上完全相同。下表突顯了三種格式之間的主要差異。
| 特性 | JSON | JSONL | NDJSON |
|---|---|---|---|
| 全稱 | JavaScript Object Notation | JSON Lines | Newline Delimited JSON |
| 副檔名 | .json | .jsonl | .ndjson |
| MIME 類型 | application/json | application/jsonl(非官方) | application/x-ndjson |
| 規格 | RFC 8259(IETF 標準) | jsonlines.org(社群) | github.com/ndjson/ndjson-spec(社群) |
| 行分隔符 | 不適用(單一值) | \n(換行符) | \n(換行符) |
| 尾隨換行符 | 不適用 | 建議 | 可選 |
| 適合串流 | 否(必須解析整個文件) | 是(逐行) | 是(逐行) |
NDJSON MIME 類型:application/x-ndjson
NDJSON 的註冊 MIME 類型是 application/x-ndjson。此內容類型在 HTTP 標頭中使用,表示回應內容包含換行分隔的 JSON 資料。許多串流 API,包括 GitHub API、Docker Registry API 和 Elasticsearch 批次 API,都使用此 MIME 類型來傳遞 NDJSON 回應。
Content-Type: application/x-ndjson# Example: curl a streaming APIcurl -H "Accept: application/x-ndjson" https://api.example.com/events/stream# Example: Express.js responseres.setHeader('Content-Type', 'application/x-ndjson');res.write(JSON.stringify(record) + '\n');
某些 API 也接受 application/json-seq(RFC 7464)或 text/plain 作為替代方案。然而,application/x-ndjson 是換行分隔 JSON 串流最廣泛採用的 MIME 類型。建構串流 JSON 記錄的新 API 時,使用 application/x-ndjson 以獲得最大相容性。
讀寫 NDJSON
在任何支援 JSON 的語言中處理 NDJSON 都很簡單。以下是 Python、Node.js 和命令列的實用範例。
Python 的內建 json 模組天然處理 NDJSON。從檔案中讀取行,用 json.loads 解析每一行,用 json.dumps 寫入。對於大型檔案,這種逐行方式使用恆定的記憶體。
guide-ndjson-complete-guide.ndjsonGuide.readWrite.python.code
在 Node.js 中,使用 readline 模組搭配 fs.createReadStream 高效解析 NDJSON 檔案。串流一次處理一行,無論檔案大小如何,記憶體使用量都保持較低。
import { createReadStream, writeFileSync } from 'node:fs';import { createInterface } from 'node:readline';// Read NDJSONasync function readNdjson(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;}// Write NDJSONconst data = [{ id: 1, event: 'page_view' },{ id: 2, event: 'click' },];const ndjson = data.map(r => JSON.stringify(r)).join('\n') + '\n';writeFileSync('output.ndjson', ndjson, 'utf-8');
jq 命令列工具原生理解 NDJSON 輸入。使用 --slurp 旗標將所有記錄收集成陣列,或不加旗標逐一處理每筆記錄。
# Print each record (jq reads NDJSON by default)jq '.' data.ndjson# Filter records where event is "click"jq 'select(.event == "click")' data.ndjson# Extract specific fieldsjq {id, url} data.ndjson# Count total recordsjq -s 'length' data.ndjson# Convert NDJSON to a JSON arrayjq -s '.' data.ndjson > data.json# Convert a JSON array back to NDJSONjq -c '.[]' data.json > data.ndjson
NDJSON 在 HTTP 串流 API 中的應用
NDJSON 是傳遞即時資料的 HTTP 串流 API 的事實標準。當伺服器發送 NDJSON 回應時,客戶端可以在第一筆記錄到達時立即開始處理,無需等待整個回應完成。這比返回大型 JSON 陣列更快且更節省記憶體。
使用 NDJSON 串流的知名服務包括 Docker Registry(映像層事件)、Elasticsearch(批次操作)、Apache CouchDB(變更訂閱)和許多現代事件驅動 API。此模式非常適合 Server-Sent Events 的替代方案、即時日誌追蹤,以及 Web 應用程式中的漸進式資料載入。
import express from 'express';const app = express();app.get('/api/events/stream', (req, res) => {res.setHeader('Content-Type', 'application/x-ndjson');res.setHeader('Transfer-Encoding', 'chunked');// Simulate streaming eventslet id = 0;const interval = setInterval(() => {id++;const event = {id,type: 'heartbeat',timestamp: new Date().toISOString(),};res.write(JSON.stringify(event) + '\n');if (id >= 100) {clearInterval(interval);res.end();}}, 100);req.on('close', () => clearInterval(interval));});app.listen(3000);
async function* readNdjsonStream(url) {const response = await fetch(url, {headers: { 'Accept': 'application/x-ndjson' },});const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();let buffer = '';while (true) {const { done, value } = await reader.read();if (done) break;buffer += value;const lines = buffer.split('\n');buffer = lines.pop();for (const line of lines) {if (line.trim()) yield JSON.parse(line);}}if (buffer.trim()) yield JSON.parse(buffer);}// Usagefor await (const event of readNdjsonStream('/api/events/stream')) {console.log('Received:', event);}
NDJSON 生態系統工具
一個不斷增長的命令列工具和函式庫生態系統原生支援 NDJSON。這些工具讓您無需撰寫自訂程式碼即可過濾、轉換和分析 NDJSON 資料。
jq
必備jq 是最流行的命令列 JSON 處理器。它預設讀取 NDJSON(每行一個 JSON 值),支援過濾、映射、分組和重新格式化。使用 jq -c 進行壓縮輸出,使用 jq -s 將所有記錄收集成陣列。
ndjson-cli
CLIndjson-cli 是一系列 Unix 風格的命令,用於操作 NDJSON 串流:ndjson-filter、ndjson-map、ndjson-reduce、ndjson-sort 和 ndjson-join。每個命令從 stdin 讀取,寫入 stdout,使其可以透過管道進行組合。
ndjson(npm)
Node.jsndjson npm 套件為 Node.js 提供串流 NDJSON 解析器和序列化器。它暴露 ndjson.parse() 和 ndjson.stringify() transform streams,可直接整合到 Node.js 串流管線中進行高吞吐量資料處理。
NDJSON 與 JSONL:互通性
NDJSON 和 JSONL(JSON Lines)是功能上完全相同的格式。兩者都是每行儲存一個 JSON 值,以換行符分隔。有效的 NDJSON 檔案同時也是有效的 JSONL 檔案,反之亦然。您可以將 .ndjson 檔案重新命名為 .jsonl(或反過來),而不需要更改一個位元組的內容,所有讀取一種格式的工具也會讀取另一種。
唯一的區別是表面上的:NDJSON 來自 github.com/ndjson/ndjson-spec,使用 .ndjson 副檔名和 application/x-ndjson MIME 類型,而 JSONL 來自 jsonlines.org,使用 .jsonl 副檔名。在實際應用中,大多數開發者將這兩個名稱視為同義詞。如果您的專案已經使用 JSONL,則無需遷移到 NDJSON;如果某個函式庫表示支援 NDJSON,它也會毫無問題地處理您的 .jsonl 檔案。
試用我們的免費 NDJSON/JSONL 工具
直接在瀏覽器中處理 NDJSON 和 JSONL 檔案。所有處理都在本機進行,因此您的資料保持私密。