JSON 数据类型深度:number 精度与字符串编码
JSON 只有六种类型,却藏着两个高频坑:大整数丢精度、字符串转义。 本文讲清 number 的 IEEE-754 本质、超范围整数的处理办法、\uXXXX 与 Unicode 的关系,以及 null 与 undefined 的差别,示例可在 JSON 格式化 工具中复现。
一、六种类型,一个陷阱
JSON 的类型是:object、 array、 string、 number、 boolean、 null。 number 不区分整数与浮点,JSON 里没有单独的整型,大整数问题就是这么来的。
二、number 的精度边界:2^53-1
多数解析器把 number 映射为 IEEE-754 双精度浮点,能精确表示的最大整数是 9007199254740991(即 2^53-1)。超过它,整数就会被就近取整:
// 输入(原样保留)
{ "orderId": 9007199254740993 }
// 关闭无损模式后(double 就近取整,末尾 3 变 2)
{ "orderId": 9007199254740992 }这种丢精度是静默的:解析不报错,只是值变了,等到对账时才发现。站内格式化工具会对输入做词法扫描, 标出所有超范围整数并给出行号列号。
三、两种办法:无损模式或改字符串
- 开启无损模式。基于 BigInt 的解析器把超范围整数字面量按原始文本承载,格式化 / 压缩时原样写回,值不变。 代价是这块解析逻辑按需动态加载,只在开启时下载。
- 把 ID 改成字符串(更推荐)。订单号、雪花 ID、手机号这类「标识符」本就不该参与算术, 用
"9007199254740993"传输能从根本上绕开精度问题,也对所有语言友好。
无损模式的边界:它把 16 位以上的数字按整数处理,遇到超长小数字面量(长度超过 15 位)会拒绝并提示「检测到超长小数字面量」。 这类值请关掉无损模式,或同样改为字符串。
四、字符串转义:\uXXXX 与 Unicode
JSON 字符串里不能出现裸换行,也不能让双引号原样出现。特殊字符必须转义:
- 换行 →
\n,制表符 →\t,双引号 →\",反斜杠 →\\。 - 控制字符按规范写作
\uXXXX,例如码位 U+0000 写\u0000。 - 中文字符本质上也是 Unicode,可以直接写字面量(如「中」),也可以写成
\u4e2d,两者等价。 - Emoji 等超出基本多文种平面的字符,UTF-16 里占两个码元,转义时是一对
\uXXXX代理对。
站内 转义 / 反转义工具 可把多行 SQL、正则、证书片段等文本转成可直接嵌入 JSON 的字符串字面量,反转义时又还原原文。
五、null 与 undefined
JSON 规范只有 null,没有 undefined。undefined 是 JavaScript 的概念, 序列化行为并不一致:对象里值为 undefined 的键会被直接丢弃,数组里则会被转成 null。因此跨语言传输时,请统一用 null 表达空值,不要依赖 undefined,否则同一个字段在不同语言里可能「时有时无」。
六、常见错误
- 用数字存 ID。超过 2^53-1 就会丢精度,标识符应一律用字符串。
- 字符串里写裸换行。引号内出现真实换行会直接语法错误,必须写
\n。 - 反斜杠数量对不上。正则
\d+嵌进 JSON 要写成\\d+,两层是正常的。 - 小数点后位数无限。普通小数本就只保证约 15 位有效数字,高精度金额应改用字符串或分位整数。
- 依赖 undefined 语义。跨语言时请用 null,避免字段静默消失。
动手跑一遍
把含 9007199254740993 的 JSON 粘进格式化工具,看行级大整数提示;开启无损模式对比关闭前后的输出差异。
常见问题
为什么我的订单号 / 雪花 ID 变了一位?
JSON 的 number 在多数语言里映射为 IEEE-754 双精度浮点,能精确表示的整数上限是 2^53-1(9007199254740991)。超过这个范围的整数会静默就近取整,例如 9007199254740993 会变成 9007199254740992。站内格式化工具会词法扫描出这类超范围整数并给出行列定位。
无损模式是怎么做到不丢精度的?
开启后,工具改用基于 BigInt 的解析器读取数字,超出安全范围的整数字面量按原始数字文本承载,序列化时再原样写回,因此数字的值不会经过 double 转换。它按需动态加载,只有真正开启无损模式时才会下载这部分代码。
无损模式有什么限制?
它把 16 位以上的数字按整数处理,遇到超长小数字面量(长度超过 15 位)或无法按整数解释的科学计数法数字时会拒绝解析并提示。这类值建议关闭无损模式,或把数字改为字符串再传输。ID 类数据本来就更适合用字符串表达。
字符串里的 \uXXXX 是什么?
JSON 字符串中的 \uXXXX 是用四位的 Unicode 码位转义一个字符,例如 \u4e2d 是「中」。控制字符(如换行 、制表符 )在 JSON 里也必须转义,不能出现裸换行。站内转义工具可把含换行、引号、反斜杠的文本转成合法字符串字面量,并把控制字符输出为 \uXXXX,同时把中文、Emoji 按原文保留。
null 和 undefined 有什么区别?
JSON 规范里只有 null,没有 undefined。undefined 是 JavaScript 语言层面的概念,序列化时会被 JSON.stringify 丢弃(对象里该键消失)或被转成 null(数组里变 null)。跨语言传输请统一用 null 表示「空值」,不要依赖 undefined。