Watch at Ease看见世界,读懂生活。

科技 · 实用指南

jq 原样输出没问题,运行过滤器却改了 ID?添加 BigInt 前先检查真实链路

原样输出测试可能通过,算术运算仍会改变末位数字。用成对的原始输出对照,在下游还原回调掩盖问题前定位信息丢失。

八组 jq 与 Node 测试的精确数字结果示意
原创实测示意图:八种路径显示提前保留与事后转换的区别,仅代表本次 jq1.7/Node24.22.1 样本。 Watch at Ease

ID 的末位改变时,先保存每个步骤的原始输出,再考虑更换解析器。一个成功的原样输出测试,不能证明实际运行的算术过滤器也保留了所有数字。

下面用八组本地测试区分源文本、jq 输出和 Node 解析三个边界,并用字符串预期值验证完整数字。

1. 不要用 Number 保存预期 ID

从原始 JSON 文本 {"id":9007199254740993,"count":42} 开始,把预期 ID 保存为字符串 "9007199254740993"。不要把它写成 JavaScript 数字字面量,再比较两个 Number:预期值本身可能已经被舍入。修改数据前,记录实际使用的 jq、Node 可执行文件、版本及生产过滤器。

RFC8259 允许实现限制数字范围和精度,并指出绝对值不超过 2^53−1 的整数可精确互通。超出这一范围,部分整数仍可表示,并非所有大数都会改变。标识符需要完整数字;解析成功或日志看起来合理,都不能作为验收依据。

2. 原样输出与真实过滤器成对测试

八组 Linux 本地测试使用 jq1.7 和 Nodev24.22.1。对于上述样本,jq -c '.' 输出原始的 ...993;过滤器 .id += 0 则输出 ...992。1.7 版手册分别说明了未修改字面量的保留方式与双精度算术。因此,原样输出通过只能证明这项操作,不能证明所有过滤器都安全。

保存每次转换前后直接输出的文本。在本次二进制程序上,.id |= tostring 得到 "9007199254740993";.id += 0 | .id |= tostring 得到 "9007199254740992"。提前与延后转字符串是两种不同对照。请用实际生产过滤器和构建版本重测;这些结果不能替其他 jq 实现或版本作保证。

3. 证明还原回调究竟收到什么

普通 JSON.parse 后调用 id.toString() 得到 ...992;对已经解析的值使用 BigInt 也得到 ...992。按键名读取 BigInt(context.source) 的还原回调,可以从原始样本及原样过滤器输出中得到 ...993。经过 jq 算术运算后,同一回调却得到 ...992,因为其输入数字文本已经改变。

对结构已知的数字 ID,测试回调是 (k,v,c)=>k==='id'&&typeof v==='number'?BigInt(c.source):v。它只用于样本,不是通用校验器。应检测实际运行环境是否支持 context.source,并验证字段路径与整数字面量语法。小数或指数形式需要另行约定;把所有像数字的字符串都转换,也会误改应保留为文本的字段。

4. 完成整个往返验证

如果能控制 API,迁移前先与接收方约定字符串 ID。第八组测试明确用 id.toString() 序列化还原出的 BigInt,再按字符串协议解析,得到全部原始数字。这证明样本能往返,不代表兼容未修改的数字类型结构。避免悄悄改变数量、小数金额或无关字段。

只有捕获的源数字文本、每个预期传递步骤,以及最终按字段协议进行的字符串比较全部一致,才能验收。保留真实过滤器回归测试和原样输出对照,记录 ...993 与 ...992,以及最先发生变化的位置。下游回调无法恢复上游丢失的信息。本次测试使用本地样本,没有测试线上网关或浏览器;请在真实生产与消费边界分别重测。

源数字、jq 输出和 Node 解析三个边界示意
原创诊断示意图:逐段比较数字文本,context.source 只能读取当前解析输入,不能还原更早丢失的信息。 Watch at Ease