DEBUG.WIKI
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →

serde_json 升级后,1e400 输出变成 1e+400

开启 arbitrary_precision 后,大指数数字仍能读取,文本快照却多了正号。先区分数值约定与原始字节约定,避免用 f64 丢掉范围。

开启 serde_json 的 arbitrary_precision 后,"1e400" 能读入 Value,但转回字符串变成了 "1e+400"。原先要求逐字相等的测试、缓存键或签名因此改变,不能只看它仍是有效 JSON 就忽略差异。

这项变化可见于 1.0.148 升级到 1.0.149 的路径,正式 1.0.154 的 arbitrary_precision 解析器也仍会为没有显式符号的指数补 +。它在扫描指数时就发生,不能简单归因于一个浮点格式化依赖升级。

Value 不承诺保留数字的原始拼写

1e400 和 1e+400 表达相同的十进制指数,但它们是不同字节。Value 用于结构化处理,不是原始 JSON 文本容器。开启 arbitrary_precision 解决大数字的表示范围,并不额外承诺原样回写每一种词法。

也不要为了比较而把它转成 f64:1e400 超过普通有限 f64 的范围,会引入另一项溢出或精度问题。直接使用 Value 或 Number 的相等运算,也不能未经核实就当成任意十进制表示的数学等价判定。

必须保留原始数字时,在读取入口保留 RawValue

若协议需要原样转发这个数字,可以从原始输入直接读取 RawValue,而不是先读成 Value 再转换:

use serde_json::value::RawValue;

fn preserve_number(input: &str) -> serde_json::Result<String> {
    let raw: &RawValue = serde_json::from_str(input)?;
    Ok(raw.get().to_owned())
}

项目需启用 serde_json 的 raw_value feature。这个函数验证输入是一个完整 JSON 值,但不额外保证它是数字;业务字段只允许数字时,应同时保留对应的类型与范围校验。不能因为 RawValue 接受了字符串、对象或 null,就把它们也作为有效金额或计数。

RawValue 保留的是值片段,不是整个 HTTP 消息的字节。若签名覆盖消息外层空白、字段顺序或其他原始字节,应直接保留被签名的原始输入,并按协议验证签名,不能用重新序列化后的内容替代。

若输入还需经过 flatten 收集未知字段,注意flatten 与 RawValue 的读取边界;换成原始值类型并不保证所有中间缓冲路径都能保留它。

只关心数值时,修改比较规则要有协议依据

文本快照测试可以改为检查项目真正承诺的结构和数值,但大数字需要支持对应范围、精度和指数的比较方式。签名或内容寻址协议若有既定规范化算法,按那个算法处理,不能自行删掉所有正号、指数零或小数点来“统一”。

回查无符号正指数、显式正负指数、大小写 E、零与业务边界的大数,同时比较原始字节用途和业务运算用途。保留旧输出样本,确认哪些下游依赖逐字表示后,再决定更新快照、调整存储格式或迁移签名约定。