@kaigejiabengle/safe-json-stream
v0.1.1
Published
Serialize arbitrarily large or deeply-nested JSON without RangeError / stack overflow, via streaming generators and Node streams.
Downloads
258
Maintainers
Readme
safe-json-stream
把任意超大 / 超深嵌套的 JSON 对象序列化成字符串,不会抛出
RangeError: Invalid string length 或 Maximum call stack size exceeded。
JSON.stringify({}) 没问题,但在生产环境里你传入的往往不是 {}——而是一个
涨到几 MB 的响应、一个有几十万项的数组、或一个嵌套了几千层的对象。到那个
量级,JSON.stringify 要么试图构建一根比 V8 上限还长的字符串,要么递归深度
超过了 JS 调用栈,然后进程就挂了。
本包用增量方式序列化同样的数据:
jsonChunks(value)—— 生成器,把 JSON 拆成一串小字符串片段逐个产出(绝 不会先攒成一根巨型字符串)。createReadable(value)—— 基于该生成器的 NodeReadable,自带背压 (back-pressure)。streamify(value, writable)—— 直接管道接到文件 / HTTP 响应,返回一个Promise。stringify(value)—— 便捷方法,返回一整根字符串(仅建议用于中等规模数据)。
它用的是显式栈而不是递归调用,所以嵌套深度不受限,且内存只占用"当前片段 + 打开中的帧",而不是整个序列化结果。
安装
npm install @kaigejiabengle/safe-json-stream本地开发时也可指向本地构建产物:
npm install /path/to/safe-json-stream。
用法
流式写入文件(处理超大数据的推荐方式)
const fs = require('node:fs');
const { streamify } = require('@kaigejiabengle/safe-json-stream');
await streamify(hugeData, fs.createWriteStream('out.json'));流式写回 HTTP 响应(Express / 原生 http)
const { createReadable } = require('@kaigejiabengle/safe-json-stream');
app.get('/export', (req, res) => {
res.setHeader('Content-Type', 'application/json');
createReadable(hugeData).pipe(res);
});ESM
import { streamify } from '@kaigejiabengle/safe-json-stream';
await streamify(hugeData, fs.createWriteStream('out.json'));自己逐片段迭代
const { jsonChunks } = require('@kaigejiabengle/safe-json-stream');
for (const chunk of jsonChunks(data)) {
process.stdout.write(chunk);
}API
| 导出 | 说明 |
| --- | --- |
| jsonChunks(value) | Generator<string>,逐段产出 JSON 片段。 |
| createReadable(value, options?) | 产出字符串片段的 Readable(带背压)。 |
| streamify(value, writable) | Promise<void>;完成时 resolve,出错时 reject。 |
| stringify(value) | string;便捷方法,会完整物化整根字符串——极端规模时避免使用。 |
行为说明
在合理范围内与 JSON.stringify 语义保持一致:
undefined/ 函数 / symbol →null;对象里的undefined属性会被省略 (和JSON.stringify完全一致)。NaN/Infinity→null。Date以及任何带toJSON()的对象,会走其toJSON()序列化。- 循环引用会被替换成字符串
"[Circular]",而不是崩溃,因此序列化总能终止。 BigInt会抛TypeError(和JSON.stringify一样)。
设计上的差异:
- 字符串不会被进一步切小(你已确认字符串本身都是安全的);只有整体结构是 流式产出的。
- 输出是完整数据——不做任何截断。需要保留每一层时使用它。
该选哪个
| 你的需求… | 用 |
| --- | --- |
| 把海量数据写入磁盘 / 网络而不崩溃 | streamify / createReadable |
| 自己手动逐片段处理 | jsonChunks |
| 要一根普通字符串,且数据量中等 | stringify |
| 丢掉深层部分、只要小体积(日志 / 调试) | 改用带深度限制的 JSON.stringify replacer |
从源码构建
本包用 TypeScript(src/index.ts)编写,编译为发布用的 dist/ 产物。你只需
修改 src/index.ts 一处即可。
npm install # devDependencies: typescript, @types/node
npm run build # src/index.ts -> dist/cjs (CJS + d.ts) 与 dist/esm (ESM)
npm test # 先 build,再运行 node:test 测试套件发布产物(均在 dist/ 下,dist/ 为 gitignore 的构建产物):
dist/cjs/index.js—— CommonJS 入口(main/require)dist/esm/index.js—— ESM 入口(module/import),通过dist/esm/package.json标记为 ESMdist/cjs/index.d.ts—— 类型声明(types)
dist/ 由构建脚本生成、不纳入版本管理(见 .gitignore)。根目录 package.json
的 files 字段只打包 dist/。
许可证
MIT
