JSONL 最佳實踐
撰寫乾淨、可靠且高效能 JSONL 檔案的完整指南。學習格式化規則、Schema 設計、錯誤處理策略和正式環境工作負載的最佳化技巧。
最後更新:2026 年 2 月
為什麼 JSONL 最佳實踐很重要
JSONL(JSON Lines)看似非常簡單:每行一個 JSON 物件,以換行符分隔。但簡單並不代表不會出錯。不一致的 Schema、編碼問題、尾隨逗號和嵌入的換行符是正式資料管線中最常見的解析失敗原因。遵循一套明確的最佳實踐可以在問題發生之前就預防它們。
本指南涵蓋了可靠地產生和消費 JSONL 資料的基本規則。無論您是在建構機器學習資料集、串流應用程式日誌,還是在服務之間交換資料,這些實踐都能幫助您避免微妙的錯誤並從 JSONL 工作流程中獲得更好的效能。
格式化規則
有效 JSONL 的基礎是嚴格遵守幾條格式化規則。違反其中任何一條都會產生大多數解析器無法處理的檔案。
JSONL 檔案中的每一行都必須是一個完整、獨立的 JSON 值。永遠不要將單一 JSON 物件拆分到多行。格式化後的 JSON 不是有效的 JSONL。始終使用緊湊格式序列化(鍵和值之間無縮排或額外空白)。
# Valid JSONL - one complete JSON per line{"id":1,"name":"Alice","tags":["admin","user"]}{"id":2,"name":"Bob","tags":["user"]}# INVALID - pretty-printed JSON spans multiple lines{"id": 1,"name": "Alice"}
JSONL 檔案必須使用 UTF-8 編碼。這是幾乎所有 JSONL 解析器、串流工具和雲端服務所假設的編碼。避免使用 UTF-16、Latin-1 或其他編碼。如果您的來源資料使用不同的編碼,請在寫入 JSONL 之前將其轉換為 UTF-8。
# Python: always specify UTF-8 when reading/writingwith open('data.jsonl', 'w', encoding='utf-8') as f:f.write(json.dumps(record, ensure_ascii=False) + '\n')# Node.js: UTF-8 is the default for fsfs.appendFileSync('data.jsonl', JSON.stringify(record) + '\n', 'utf-8');
使用單一換行字元(LF,\n)作為行分隔符。這是 Linux、macOS 和大多數雲端環境中的標準。避免使用 Windows 的回車加換行(CRLF,\r\n),因為它可能導致解析問題。大多數現代編輯器和工具會自動處理這個問題,但如果您在跨平台工作,請檢查您的設定。
# Correct: LF line endings (\n){"id":1}\n{"id":2}\n# Avoid: CRLF line endings (\r\n){"id":1}\r\n{"id":2}\r\n# Tip: configure Git to normalize line endings# .gitattributes*.jsonl text eol=lf
Schema 一致性
雖然 JSONL 不強制要求 Schema,但在記錄間保持一致性使您的資料更容易處理。不一致的 Schema 會導致執行時期錯誤、意外的 null 值和匯入失敗。
在所有記錄中保持相同的欄位名稱、欄位順序和值類型。雖然 JSON 不要求欄位排序,但一致的排序可以提高可讀性和可壓縮性。永遠不要對同一欄位混用類型(例如,"price" 欄位不應在某些記錄中是字串,在其他記錄中是數字)。
# Good: consistent field order and types{"id":1,"name":"Alice","age":30,"active":true}{"id":2,"name":"Bob","age":25,"active":false}{"id":3,"name":"Charlie","age":35,"active":true}# Bad: inconsistent order, mixed types, missing fields{"name":"Alice","id":1,"active":true}{"id":"2","age":25,"name":"Bob"}{"id":3,"active":"yes","name":"Charlie"}
當欄位沒有值時,使用 JSON null 而不是省略該鍵。這使得下游處理更加簡單,因為每筆記錄都有相同的鍵集合。消費者不需要區分「欄位缺失」和「欄位為 null」。
# Good: include all fields, use null for missing values{"id":1,"name":"Alice","email":"alice@example.com","phone":null}{"id":2,"name":"Bob","email":null,"phone":"+1-555-0100"}# Avoid: omitting keys for missing data{"id":1,"name":"Alice","email":"alice@example.com"}{"id":2,"name":"Bob","phone":"+1-555-0100"}
錯誤處理
現實中的 JSONL 檔案由於編碼故障、截斷寫入或上游錯誤,經常包含少量無效行。健壯的消費者會優雅地處理這些問題,而不是在遇到第一行錯誤時就崩潰。
將每行的解析操作包裝在 try-catch 區塊中,並記錄任何失敗的行號和錯誤訊息。這讓您可以跳過無效行,同時保留問題記錄。對於關鍵管線,將錯誤行收集到單獨的檔案中以便日後檢查。
import jsondef parse_jsonl_safe(path: str):"""Parse JSONL with error tolerance."""valid, errors = [], []with open(path, 'r', encoding='utf-8') as f:for line_num, line in enumerate(f, 1):line = line.strip()if not line:continuetry:valid.append(json.loads(line))except json.JSONDecodeError as e:errors.append({'line': line_num, 'error': str(e), 'raw': line})print(f'Parsed {len(valid)} records, {len(errors)} errors')return valid, errors
對於資料管線,在主要處理邏輯之前新增驗證步驟。檢查每筆記錄是否具有預期的欄位和類型。拒絕或隔離不符合的記錄。這可以防止在管線深處出現類型錯誤,那裡的問題更難除錯。
def validate_record(record: dict) -> list[str]:"""Validate a JSONL record against expected schema."""issues = []required = ['id', 'name', 'timestamp']for field in required:if field not in record:issues.append(f'Missing required field: {field}')if 'id' in record and not isinstance(record['id'], int):issues.append(f'Field "id" should be int, got {type(record["id"]).__name__}')return issues# Usage in pipelinefor record in parse_jsonl_safe('data.jsonl')[0]:issues = validate_record(record)if issues:log_warning(f'Record {record.get("id")}: {issues}')else:process(record)
效能最佳化
在資料工程和機器學習工作流程中,JSONL 檔案可能增長到數 GB。正確的處理策略可以保持記憶體使用量受控且吞吐量較高。
永遠不要一次將整個 JSONL 檔案載入記憶體。一次讀取和處理一行(或一批行)。這使記憶體使用量保持恆定,與檔案大小無關。Python 的檔案迭代天然是逐行的,Node.js 有 readline 和 stream API 實現相同目的。
# Python: stream with constant memoryimport jsoncount = 0with open('large.jsonl', 'r', encoding='utf-8') as f:for line in f: # One line at a time, not f.readlines()!record = json.loads(line)process(record)count += 1print(f'Processed {count} records')
寫入資料庫或呼叫 API 時,將多筆記錄批次處理而不是逐筆處理。批次處理減少 I/O 開銷,可將吞吐量提升 10-100 倍。對於大多數使用情境,每批 1,000 到 10,000 筆記錄效果良好。
import jsondef process_in_batches(path: str, batch_size: int = 5000):"""Process JSONL records in batches for better throughput."""batch = []with open(path, 'r', encoding='utf-8') as f:for line in f:line = line.strip()if not line:continuebatch.append(json.loads(line))if len(batch) >= batch_size:bulk_insert(batch) # Send batch to databasebatch.clear()if batch:bulk_insert(batch) # Flush remaining records
JSONL 的壓縮效果非常好,因為相鄰的行通常共享相同的鍵和相似的值。使用 gzip 進行儲存和傳輸可以將檔案大小減少 5-10 倍。大多數程式語言可以直接讀取 gzip 壓縮的 JSONL,無需先解壓縮到磁碟。
import gzipimport json# Write compressed JSONLwith gzip.open('data.jsonl.gz', 'wt', encoding='utf-8') as f:for record in records:f.write(json.dumps(record, ensure_ascii=False) + '\n')# Read compressed JSONLwith gzip.open('data.jsonl.gz', 'rt', encoding='utf-8') as f:for line in f:record = json.loads(line)process(record)
應避免的常見錯誤
這些是使用者使用我們的工具驗證 JSONL 檔案時最常見的問題。每個問題都會導致解析失敗,如果沒有正確的方法可能難以診斷。
JSON 不允許在物件或陣列的最後一個元素後面有尾隨逗號。這是最常見的錯誤之一,尤其對於來自 JavaScript 的開發者,因為 JavaScript 允許尾隨逗號。始終從輸出中移除尾隨逗號。
# INVALID: trailing comma after last property{"id": 1, "name": "Alice",}# VALID: no trailing comma{"id": 1, "name": "Alice"}# INVALID: trailing comma in array{"tags": ["admin", "user",]}# VALID: no trailing comma in array{"tags": ["admin", "user"]}
如果字串值包含原始換行字元,它將破壞每行一筆記錄的規則並損壞您的 JSONL 檔案。始終在 JSON 字串中使用跳脫形式 \n,而不是原始換行符。大多數 JSON 序列化器會自動處理這個問題,但手動建構 JSON 字串時要注意。
# INVALID: raw newline inside a string value breaks JSONL{"id": 1, "bio": "Line oneLine two"}# VALID: escaped newline keeps everything on one line{"id": 1, "bio": "Line one\nLine two"}# Tip: json.dumps() in Python handles this automaticallyimport jsonrecord = {"bio": "Line one\nLine two"}print(json.dumps(record))# Output: {"bio": "Line one\nLine two"}
在同一檔案中混合使用 UTF-8 和 Latin-1(或其他編碼)會產生亂碼字元和解析錯誤。這通常發生在從不同來源附加資料時。寫入前始終正規化為 UTF-8。如果您收到未知編碼的資料,在轉換前使用 chardet 等套件偵測編碼。
# Python: detect and convert encodingimport chardetdef normalize_to_utf8(input_path: str, output_path: str):"""Detect encoding and convert to UTF-8."""with open(input_path, 'rb') as f:raw = f.read()detected = chardet.detect(raw)encoding = detected['encoding'] or 'utf-8'print(f'Detected encoding: {encoding}')text = raw.decode(encoding)with open(output_path, 'w', encoding='utf-8') as f:f.write(text)
檔案命名與組織
良好的檔案命名和目錄結構使 JSONL 資料更容易被發現、管理和在自動化管線中處理。
使用 .jsonl 作為預設副檔名。它是 JSON Lines 檔案最廣泛認可的副檔名,也是 OpenAI 微調 API、BigQuery 和大多數資料平台所預期的。.ndjson 副檔名(Newline Delimited JSON)技術上是相同格式的不同名稱。在專案中選擇一種慣例並堅持使用。
# Recommended file naming conventionsdata.jsonl # Standard JSONL fileusers_2026-02-14.jsonl # Date-stamped exporttrain.jsonl # ML training datavalidation.jsonl # ML validation splitevents.jsonl.gz # Compressed JSONL
按用途和日期組織 JSONL 檔案。將原始輸入資料與處理後的輸出分開。對時間序列或日誌資料使用基於日期的分區,便於處理特定日期範圍和清理舊資料。
project/data/raw/ # Original unprocessed filesevents_2026-02-13.jsonlevents_2026-02-14.jsonlprocessed/ # Cleaned and transformedevents_clean.jsonlschemas/ # Schema documentationevent_schema.jsonscripts/validate.py # Validation scripttransform.py # Transformation pipeline
線上驗證您的 JSONL 檔案
將這些最佳實踐付諸行動。使用我們的免費線上工具直接在瀏覽器中驗證、格式化和檢查您的 JSONL 檔案。