JSON 解析與處理庫選型對照
結論:原生 API 最快,容錯解析帶偏移量,路徑查詢與補丁各管一段。選型看三件事:錯誤定位、是否破壞原文、邊界是否明確。

結論:先按用途分四類再看實現——只讀值用原生 JSON.parse,需要偏移量或保留原文格式用 jsonc-parser,查詢與補丁庫各自按需引入;體積大的庫按頁面動態匯入,長期不維護的庫直接排除。
每個庫都宣稱自己「快、小、全」。真正區分它們的是出錯時能告訴你多少資訊。
一、按用途分四類
| 類別 | 代表庫 | 解決什麼 |
|---|---|---|
| 標準解析 | JSON.parse / JSON.stringify |
語法 ↔ 物件,速度優先 |
| 容錯解析 + 編輯 | jsonc-parser、json5 |
帶註釋的輸入、精確到偏移量的錯誤、保留格式的修改 |
| 查詢 | jsonpath-plus、jsonpath |
按表示式篩選節點 |
| 變更 | fast-json-patch、immer |
生成/應用補丁,不可變更新 |
先確定自己在哪一類,再比較具體實現,否則很容易拿查詢庫去幹解析的活。
二、橫向對照
| 庫 | 錯誤定位 | 容錯(註釋/尾逗號) | 能否保留原文格式 | 適用場景 |
|---|---|---|---|---|
JSON.parse |
只有訊息與位置(引擎相關) | ✗ | ✗ | 預設解析、介面資料 |
jsonc-parser |
精確 offset + 錯誤碼 |
✓ | ✓(modify + applyEdits) |
編輯器、配置檔案 |
json5 |
行列資訊 | ✓(更寬鬆) | ✗ | 讀取 JSON5 配置 |
jsonlint |
行列資訊 | ✗ | ✗ | 僅作參考,維護不活躍 |
jsonpath-plus |
表示式語法錯誤 | — | — | 按表示式查詢 |
fast-json-patch |
操作級錯誤 | — | — | 補丁生成與應用 |
lz-string |
— | — | — | 把 JSON 壓縮排 URL |
JSON.parse 的錯誤資訊在不同引擎差異很大。V8 會給出類似
Unexpected token } in JSON at position 42 的提示,但位置是否準確取決於引擎版本;
jsonc-parser 則穩定返回結構化的 { error, offset, length },適合做編輯器標記。
三、四個關鍵決策點
3.1 需不需要偏移量
做編輯器、要做「點錯誤跳轉到對應行」就必須有 offset。判斷方法很簡單: 如果你的錯誤提示只能列印「JSON 格式錯誤」,使用者就得自己找半天。
import { parse, printParseErrorCode } from 'jsonc-parser';
const errors: Array<{ error: number; offset: number; length: number }> = [];
parse(text, errors, { allowTrailingComma: true, disallowComments: false });
if (errors.length > 0) {
const firstError = errors[0];
console.error(`${printParseErrorCode(firstError.error)} at offset ${firstError.offset}`);
}
3.2 要不要保留原文
只想讀值 → 任何解析器都行。 要「改一個欄位、其餘原樣保留」→ 必須用編輯式 API,不能用「解析 → 改物件 → 序列化」。
後者會丟掉:註釋、鍵順序(部分實現)、縮排風格、數字字面量寫法(1e3 變 1000)、轉義形式(\u4e2d 變中文)。
3.3 邊界行為是否明確
選庫前查三件事的文件:
- 重複鍵怎麼處理(取最後一個 / 報錯);
- 大整數是否丟精度(多數 JS 庫會,因為都走
Number); - BOM、註釋、尾逗號的支援範圍。
沒有文件說明的,就寫個小樣例實測。這比讀 README 更可靠。
3.4 維護狀態
JSON 庫的風險不在功能缺失,而在長期不維護:安全補丁(原型汙染、ReDoS)不會有人發。
四、其他語言的對照
| 語言 | 標準庫 / 主流庫 | 關鍵差異 |
|---|---|---|
| JavaScript | JSON.parse |
數字一律雙精度 |
| TypeScript | 同上 + jsonc-parser / zod |
型別只在編譯期 |
| Go | encoding/json |
預設 float64,需 UseNumber() 保精度;json.RawMessage 延遲解析 |
| Java | Jackson / Gson / Moshi | Jackson 需注意預設型別開啟的風險 |
| Python | json / orjson |
parse_float=Decimal 保精度;orjson 更快但不支援所有型別 |
| Rust | serde_json |
強型別反序列化;arbitrary_precision 特性保精度 |
| C++ | simdjson / RapidJSON | simdjson 面向解析吞吐,RapidJSON 更通用 |
跨語言交換時,最容易踩的還是數字精度與時間格式,庫的選擇只能緩解不能消除。
五、典型組合方案
方案 1:工具站(瀏覽器端)
解析與錯誤定位:jsonc-parser
編輯器:CodeMirror 6 + @codemirror/lang-json
查詢:jsonpath-plus
補丁:fast-json-patch
分享連結:lz-string
這套組合的特點是全在本地跑,不上傳資料,符合工具類站點的隱私預期。
方案 2:服務端介面閘道器
校驗:Ajv(schema 編譯一次,複用)
解析:原生 JSON.parse(配合 body 大小限制)
大欄位延遲解析:保留原始字串,用時再 parse
閘道器層不要引入查詢/補丁庫做業務邏輯,那是業務層的事。
方案 3:CLI 與指令碼
取欄位:jq
格式轉換:yq / dasel
互動瀏覽:jless
批次校驗:ajv-cli
六、體積與效能的實際影響
jsonc-parser體積很小,適合進主包;json5比jsonc-parser略大,因為要支援更多語法;jsonpath-plus包含表示式求值,體積明顯更大,應隨頁面懶載入;fast-json-patch體積中等,只有對比功能時才需要。
最佳化手段很直接:按頁面動態匯入,別把它們塞進首屏 chunk。
const { JSONPath } = await import('jsonpath-plus');
七、檢查清單
- [ ] 按用途選庫,不用查詢庫做解析;
- [ ] 需要精確錯誤定位時用
jsonc-parser; - [ ] 需要保留註釋與格式時用編輯式 API;
- [ ] 實測重複鍵、大整數、BOM 三個邊界行為;
- [ ] 關注庫的維護狀態與安全通告;
- [ ] 大體積庫按需動態匯入。
常見問題
如何依專案規模選擇 JSON 函式庫?
小到只讀介面資料的工具,用原生 JSON.parse 就夠;需要編輯器式錯誤定位或保留原文格式的中型專案,選 jsonc-parser;伺服器端閘道器只做校驗與解析,查詢和補丁庫留給業務層;CLI 與指令碼直接用 jq、yq、ajv-cli。按用途分類之後再比較具體實現,能避免拿查詢庫去幹解析的活。
為什麼維護狀態比功能多寡更值得關注?
JSON 庫真正的風險不在功能缺失,而在長期不維護:原型汙染、ReDoS 這類安全補丁不會有人發。功能再全,只要維護停擺,出問題時只能自己打補丁。選型時把維護狀態與安全通告當成硬指標,比對照功能表更有效。
能否用 JSON.parse 改欄位並保留註釋與縮排?
不能。「解析 → 改物件 → 序列化」會丟掉註釋、鍵順序、縮排風格、數字字面量寫法(1e3 變 1000)和轉義形式(\u4e2d 變中文)。要改一個欄位、其餘原樣保留,必須用 jsonc-parser 的 modify 與 applyEdits 這類編輯式 API。