執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

JSONL

使用 Bun 內建的流式解析器解析換行分隔的 JSON(JSONL)

Bun 內建支援解析 JSONL(換行分隔的 JSON),其中每一行都是一個獨立的 JSON 值。解析器使用 JavaScriptCore 的最佳化 JSON 解析器以 C++ 實現,並支援流式處理。

TypeScript
const results = Bun.JSONL.parse('{"name":"Alice"}\n{"name":"Bob"}\n');
// [{ name: "Alice" }, { name: "Bob" }]

Bun.JSONL.parse()

解析完整的 JSONL 輸入並傳回所有解析值的陣列。

TypeScript
import { JSONL } from "bun";

const input = '{"id":1,"name":"Alice"}\n{"id":2,"name":"Bob"}\n{"id":3,"name":"Charlie"}\n';
const records = JSONL.parse(input);
console.log(records);
// [
//   { id: 1, name: "Alice" },
//   { id: 2, name: "Bob" },
//   { id: 3, name: "Charlie" }
// ]

輸入可以是字串或 Uint8Array:

TypeScript
const buffer = new TextEncoder().encode('{"a":1}\n{"b":2}\n');
const results = Bun.JSONL.parse(buffer);
// [{ a: 1 }, { b: 2 }]

對於 Uint8Array 輸入,Bun 會跳過緩衝區開頭的 UTF-8 BOM。

錯誤處理

如果輸入包含無效 JSON,且沒有成功解析任何值,Bun.JSONL.parse() 會丟擲 SyntaxError。如果在出錯前至少解析了一個值,則會傳回已解析的值,而不會丟擲異常。

TypeScript
try {
  Bun.JSONL.parse("{invalid}\n");
} catch (error) {
  console.error(error); // SyntaxError: 解析 JSONL 失敗
}

Bun.JSONL.parseChunk()

對於流式處理,parseChunk 會盡可能多地解析輸入中的完整值,並報告解析到的位置。這樣,當資料逐步到達時(例如來自網路流),你就知道從哪裡繼續處理。

TypeScript
const chunk = '{"id":1}\n{"id":2}\n{"id":3';

const result = Bun.JSONL.parseChunk(chunk);
console.log(result.values); // [{ id: 1 }, { id: 2 }]
console.log(result.read); // 17 — 已消費字元數
console.log(result.done); // false — 仍有不完整的值
console.log(result.error); // null — 無解析錯誤

傳回值

parseChunk 傳回一個包含四個屬性的物件:

屬性型別說明
valuesany[]成功解析的 JSON 值陣列
readnumber消耗的位元組數(Uint8Array)或字元數(字串)
doneboolean如果整個輸入被完全消費且無剩餘資料,值為 true
errorSyntaxError | null解析錯誤,若無錯誤則為 null

流式範例

使用 read 擷取已消耗輸入,剩餘部分繼續傳遞:

TypeScript
let buffer = "";

async function processStream(stream: ReadableStream<string>) {
  for await (const chunk of stream) {
    buffer += chunk;
    const result = Bun.JSONL.parseChunk(buffer);

    for (const value of result.values) {
      handleRecord(value);
    }

    // 保留未消費部分
    buffer = buffer.slice(result.read);
  }

  // 處理剩餘資料
  if (buffer.length > 0) {
    const final = Bun.JSONL.parseChunk(buffer);
    for (const value of final.values) {
      handleRecord(value);
    }
    if (final.error) {
      console.error("最終資料塊解析錯誤:", final.error.message);
    }
  }
}

使用 Uint8Array 的位元組偏移

當輸入為 Uint8Array 時,可以傳入可選的起始和結束位元組偏移:

TypeScript
const buf = new TextEncoder().encode('{"a":1}\n{"b":2}\n{"c":3}\n');

// 從位元組 8 開始解析
const result = Bun.JSONL.parseChunk(buf, 8);
console.log(result.values); // [{ b: 2 }, { c: 3 }]
console.log(result.read); // 23

// 解析特定範圍
const partial = Bun.JSONL.parseChunk(buf, 0, 8);
console.log(partial.values); // [{ a: 1 }]

read 值始終是原始緩衝區中的位元組偏移量。將它與 TypedArray.subarray() 結合使用,可以實現零複製流式處理:

TypeScript
let buf = new Uint8Array(0);

async function processBinaryStream(stream: ReadableStream<Uint8Array>) {
  for await (const chunk of stream) {
    // 追加 chunk 到緩衝區
    const newBuf = new Uint8Array(buf.length + chunk.length);
    newBuf.set(buf);
    newBuf.set(chunk, buf.length);
    buf = newBuf;

    const result = Bun.JSONL.parseChunk(buf);

    for (const value of result.values) {
      handleRecord(value);
    }

    // Keep unconsumed bytes
    buf = buf.subarray(result.read);
  }
}

錯誤恢復

與 parse() 不同,parseChunk() 遇到無效 JSON 不丟擲異常,而是通過傳回的 error 屬性提供錯誤資訊,同時傳回錯誤前成功解析的所有值:

TypeScript
const input = '{"a":1}\n{invalid}\n{"b":2}\n';
const result = Bun.JSONL.parseChunk(input);

console.log(result.values); // [{ a: 1 }] — 錯誤前解析的值
console.log(result.error); // SyntaxError
console.log(result.read); // 7 — 最後成功解析的位置

支援的值型別

每一行可以是任意有效的 JSON 值,而不僅限於物件:

TypeScript
const input = '42\n"hello"\ntrue\nnull\n[1,2,3]\n{"key":"value"}\n';
const values = Bun.JSONL.parse(input);
// [42, "hello", true, null, [1, 2, 3], { key: "value" }]

效能說明

  • ASCII 快速路徑:Bun 使用零分配的 StringView 直接解析純 ASCII 輸入,無需複製。
  • UTF-8 支援:Bun 會將非 ASCII 的 Uint8Array 輸入解碼為 UTF-16。
  • BOM 處理:Bun 會自動跳過 Uint8Array 開頭的 UTF-8 BOM(0xEF 0xBB 0xBF)。
  • 預建置物件結構:parseChunk 傳回的物件使用快取結構,以加快屬性訪問速度。