侧边栏壁纸
博主头像
一笑痕

人生若只如初见,
是可喜亦或者是可悲?

  • 累计撰写 253 篇文章
  • 累计收到 7 条评论

JSON 里字节还在,Buffer 类型没回来

2026-10-28 / 0 评论 / 3 阅读

JSON 里字节还在,Buffer 类型没回来

日志里一段 Buffer 进了 JSON,接收端解析完却拿到普通对象。字节看起来还在,类型已经变了。Node 还有个不太显眼的行为:这个对象再交给 Buffer.from,可以重新得到 Buffer。两种「恢复」写法的字节一致,引用关系却都与原对象无关。

实验用 5 个边界值组成原始数据,序列化并解析后,分别把整个解析对象和其中的 data 数组交给 Buffer.from。随后只改新 Buffer 的第一个字节,观察另外两份数据有没有跟着变。

输出里留下了类型标记

node=v26.8.1
json={"type":"Buffer","data":[0,1,127,128,255]}
parsed.type=Buffer
parsed.data=[0,1,127,128,255]
restored.hex=00017f80ff
restored.equalsOriginal=true
fromData.equalsOriginal=true
restored===original=false
afterMutate.restoredFirst=42
afterMutate.originalFirst=0
afterMutate.fromDataFirst=0

JSON 文本保存的是 {"type":"Buffer","data":[...]} 这个对象,不会保留 Buffer 的内存布局。JSON.parse 只负责解析 JSON,所以 parsed 仍是普通对象。类型标记和数字数组都在,二进制类型并没有自动回来。

操作 得到的内容 与 original 是同一对象
JSON.parse(json) 普通对象,含 type 与 data 否
Buffer.from(parsed) Buffer,十六进制 00017f80ff 否
Buffer.from(parsed.data) Buffer,十六进制 00017f80ff 否

表里第二、三行在本次输入上字节一致。第一行还不是 Buffer。对象结构留在 JSON 里,不代表解析器能凭空知道业务上希望恢复成什么类型;恢复动作发生在 Buffer.from 这里。

传给 Buffer.from(parsed) 时,Node 识别了这类 Buffer JSON 形状。实测结果与 Buffer.from(parsed.data) 相同,十六进制内容是 00017f80ff。前一种写法少写了 .data,但它依赖对象确实有可识别的 type 和 data;随手拼出的同名字段不应该未经校验就信任。

字节相同,不代表共享内存

equals 比较的是内容,因此两个恢复结果都与原始 Buffer 相等。=== 比较对象身份,恢复结果和原对象不是同一份。把恢复后的首字节改成 42,原始 Buffer 与从 data 单独构造的 Buffer 仍从 0 开始。

这个区别在消息队列或缓存里容易被误读。序列化再解析得到的是新值,不是原 Buffer 的别名;修改接收端的数据不会回写发送端的内存。若程序需要共享可变状态,JSON 通道本身就不提供这种语义。

恢复前先验证输入

Buffer.from(parsed) 的方便不等于验证。来自外部的数据仍要检查 type、data 是否存在,数组长度是否在业务允许范围内,每个元素是否为有效字节。尤其不能把客户端发来的任意对象直接当作可信的 Buffer 序列化结果。否则,超大数组可能先占用内存,异常元素也可能导致意外转换。

代码可直接用本机 Node 运行:

import { Buffer } from 'node:buffer';
const original = Buffer.from([0, 1, 127, 128, 255]);
const parsed = JSON.parse(JSON.stringify(original));
const restored = Buffer.from(parsed);
console.log(parsed.type, restored.toString('hex'));

本机结果来自 Node v26.8.1,测试输入只有 5 个字节。这证明的是这次运行里 Node 对 Buffer JSON 形状的处理,不能代替接口约定。跨语言接收端未必把 type、data 当作特殊字段;即便两边都用 Node,也应该确认实际传输没有把数组改写。

我不把这种对象形式当作通用的 JSON 解码协议。来自外部的数据仍需检查 type、data 是否存在,数组长度是否在业务允许范围内,每个元素是否为有效字节。超长数组会先吃掉内存,异常元素也可能触发意外转换。只在 Node 服务之间传递已经约定的 Buffer JSON 时,Buffer.from 省掉手动拆字段;需要长期稳定的接口,就显式定义 schema 和编码方式。

    🤞 分享