JSON 键排序

按字母顺序递归排序所有对象的键名

JSON 键排序:为什么要排,以及排序会破坏什么

排序是安全的——在语义层面

JSON 规范规定对象是无序的键值集合,键的出现顺序不携带任何语义。所以把一个对象的键按字母排序,得到的仍然是同一份数据,任何合法的解析器都会认为两者等价。

这一点让排序成为一种安全的规范化手段。它的三个主要用途:

  • 让 diff 变干净。两边的键顺序一致后,文本 diff 就只会标出真正变化的字段,而不会因为顺序差异满屏红绿。这对配置文件的版本管理和接口响应的对比非常有用。
  • 让输出可复现。构建产物、代码生成、快照测试里,同样的输入必须产生逐字节相同的输出,否则 CI 会一直报假差异。
  • 让哈希稳定。JSON 常用于生成签名和缓存键。键顺序不同会导致序列化字符串不同,从而得到不同的哈希——同一个对象算出两个签名,缓存全部失效、签名校验失败。排序(配合数字格式规范化)是解决这个问题的标准做法,这个流程叫 JSON 规范化,RFC 8785(JCS)给出了完整定义。

排序会破坏什么

规范说顺序无意义,但现实里有很多东西依赖顺序:

  • 数组的顺序绝对不能动。数组是有序集合,[1,2,3] 和 [3,2,1] 是不同的数据。排序时必须只处理对象的键,把数组原样保留。如果工具对数组元素也排序,它会静默地改掉你的数据。
  • 展示顺序。有些系统直接按 JSON 的键顺序渲染表单、表格列或菜单项。排序会让你精心安排的界面顺序变成字母序。
  • 依赖插入顺序的下游。某些语言的对象实现(如 JavaScript 对字符串键)会保留插入顺序,而有些业务逻辑会利用这一点做隐式排序。排序会改变这些行为。
  • 人工维护的配置。把相关配置项排在一起的意图(比如把 host 和 port 挨着写)会被字母序打散,可读性反而下降。

结论:排序适合"机器消费"的 JSON(签名、缓存键、快照、CI 产物),不适合"人阅读和维护"的配置,除非你接受字母序带来的可读性损失。

排序规则里的细节

问题处理方式
大小写混排区分大小写时大写字母排在小写之前(ASCII 序),Z 在 a 前面。要人眼友好可先统一大小写再排。
数字键"10" 按字符串排在 "9" 之前。要自然序需要按数值比较。
非 ASCII 键按 UTF-16 码元比较与按 Unicode 码点比较,结果可能不同(涉及代理对)。要求跨语言一致时须明确指定。
嵌套对象需要区分"只排顶层"和"递归排每一层"。递归排才是规范化需要的。

另外要注意:排序不解决重复键。JSON 里出现两个同名键时,排序会把它们排在一起,但数据依然是有问题的(规范未定义重复键行为,多数实现保留最后一个)。发现重复键应该去修数据,而不是排序。

工具怎么用

工具提供顶层排序与递归排序两种模式,可选升序/降序、以及是否保留数组原有顺序(建议保留)。解析失败时会直接报语法错误并给出位置,不会输出被静默改坏的结果。

常见问题

排序会改变数据含义吗?

不会。JSON 对象的键顺序在语义上没有意义,排序仅让输出更易读、方便版本控制中做 diff。

数组会被排序吗?

不会,数组元素顺序是有意义的,本工具只排序对象的键名,数组保持原样。