深入JSONC源码:按键映射压缩算法原理逐行解析
【免费下载链接】JSONCJSON compressor and decompressor项目地址: https://gitcode.com/gh_mirrors/json/JSONC
在网络请求的世界里,数据量每多一分,用户体验就慢一秒。当浏览器需要向服务器发送大量 JSON 数据时,冗长的字段名往往占据了传输体积的大半。JSONC正是一款专为解决此问题而生的 JSON 压缩与解压工具库,它的核心秘密就是按键映射压缩算法——把长长的键名换成单个字符,再附带一张"对照表"轻松还原。本文不聊复杂理论,带你直接走进 src/JSONC.js 源码,逐行看懂这套精妙的按键映射压缩算法原理。
一、JSONC 是什么?一套 JSON 压缩与解压的轻量方案
JSONC(JSON Compressor)由JSONC.compress/JSONC.decompress与JSONC.pack/JSONC.unpack两组 API 组成:
- 按键映射压缩:
compress用短键替换长键,压缩率可达7.5% ~ 32.81%; - Gzip 深度压缩:
pack在此基础上叠加 gzip + Base64,让数据体积再降一档。
整个库零依赖、源码只有三百多行,非常适合浏览器端直传数据、接口瘦身等场景。项目还提供了 Benchmark 演示目录 与 PHP 端解压实现,方便前后端配合使用。
二、核心思路:按键映射压缩算法究竟做了什么?
看代码前先建立直觉。假设原始 JSON 长这样:
{ "userName": "Tom", "userEmail": "tom@x.com" }userName、userEmail各 8~10 个字符,但字段名在大量数据里会重复出现千百次。按键映射压缩算法的思路很直白:
- 遍历整个 JSON,收集所有不重复的键名;
- 给每个键名分配一个超短代号(
A、B、C…); - 用代号替换原键名,同时把对照关系存进一个
_属性里; - 解压时读取
_,把代号反向替换回原名即可。
压缩后的结果类似{ _: { A: "userName", B: "userEmail" }, A: "Tom", B: "tom@x.com" }。这就是按键映射压缩算法的全貌,接下来逐行拆解实现。
三、短键从哪来?解析_numberToKey代号生成器
先看最有趣的部分——如何给第 N 个键生成短代号,见 src/JSONC.js:
var sKeys = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=_!?()*'; while (currentChar >= totalChar) { aArr.push(sKeys.charCodeAt((currentChar % totalChar) + offset)); currentChar = Math.floor(currentChar / totalChar - 1); } aArr.push(sKeys.charCodeAt(currentChar + offset)); return aArr.reverse();这里用了一个 66 字符的"字母表"(大小写字母 + 数字 + 特殊符号)。_numberToKey本质上是一个66 进制转换器:序号 0 →A、25 →Z、66 →BA,以此类推。这样理论上能表示66 + 66² + 66³…个键名,且每个键名都极短。生成的是 ASCII 码数组,再由_getSpecialKey用String.fromCharCode还原成字符串。
四、键名怎么收集?解析_getKeys遍历器
有了代号生成器,下一步就是收集所有键名,对应 src/JSONC.js:
for (sKey in json) { if (json.hasOwnProperty(sKey)) { oItem = json[sKey]; if (_isObject(oItem) || _isArray(oItem)) { aKeys = aKeys.concat(unique(_getKeys(oItem, aKeys))); // 递归深入嵌套 } if (isNaN(Number(sKey))) { // 跳过纯数字下标(数组索引) if (!contains(aKeys, sKey)) { _nCode += 1; aKeys.push([_getSpecialKey(_numberToKey(_nCode)), sKey]); } } } }几个值得注意的设计点:
- 递归遍历:遇到对象或数组就深入一层,嵌套再深也能收集到;
- 跳过数字键:数组的
0、1、2索引不参与映射,避免无意义开销; - 去重:借助
unique与contains保证同一键名只分配一次代号; - 静态计数器
_nCode:每次compress前会重置为-1,保证代号从A重新分配。
五、压缩主流程:_compressOther如何完成键替换?
键收集完毕,真正的"换装"发生在_compressOther,见 src/JSONC.js:
oKeys = _biDimensionalArrayToObject(aKeys); // 二维数组转映射表 str = JSON.stringify(json); // 序列化为字符串 for (nIndex = 0; nIndex < nLenKeys; nIndex++) { aKey = aKeys[nIndex]; str = str.replace(new RegExp(escapeRegExp('"' + aKey[1] + '"'), 'g'), '"' + aKey[0] + '"'); // 全局替换键名 } obj = JSON.parse(str); obj._ = oKeys; // 附上映射表 return obj;流程相当巧妙:先把 JSON序列化成字符串,用正则把所有"长键名"替换为"短代号",再反序列化回对象,最后把映射表挂到_属性上。escapeRegExp负责转义键名里的特殊字符,避免正则误伤。压缩后的体积如何?实测数据可以参考 Benchmark 目录下的 demo,原始 19031 字节的对象经按键映射压缩后仅 12787 字节,压缩率高达 32.81%。
六、反向还原:_decompressOther的解压逻辑
解压是压缩的镜像操作,见 src/JSONC.js:
oKeys = JSON.parse(JSON.stringify(jsonCopy._)); // 取出映射表 delete jsonCopy._; // 移除 _ 属性 str = JSON.stringify(jsonCopy); for (sKey in oKeys) { str = str.replace(new RegExp('"' + sKey + '"', 'g'), '"' + oKeys[sKey] + '"'); } return JSON.parse(str); // 还原为原始 JSON逻辑一目了然:先拷贝一份数据(避免污染原对象),取出_映射表并删除,再把字符串里的短代号批量替换回原名。注意compress/decompress对数组有专门分支_compressArray/_decompressArray,会逐个元素递归处理,保证数组套对象的场景也能正确压缩还原。这一点在 test/JSONC.js 的测试用例里有完整覆盖。
七、更进一步:pack / unpack 与 gzip 的强强联合
按键映射压缩已经足够惊艳,但 JSONC 还能再进一步。JSONC.pack在 src/JSONC.js 中把压缩后的 JSON 序列化后交给gzip(level 9 最大压缩)再用Base64编码,JSONC.unpack则反向解包。由于 Base64 编码对 URL 安全,浏览器向服务器直传 gzip 数据也能畅通无阻,服务端只需配合 GzipJSON.php 中十几行的gzinflate就能解压还原。
八、压缩效果实测:一组来自官方 Benchmark 的数据
上图 Benchmark 展示了两种典型对象在 6 种处理方式下的传输体积对比(数据来自 Benchmark/Benchmark_Results.png):
| 处理方式 | Obj2 发送体积 | 相比最重方案的提升 |
|---|---|---|
| simple json stringify(不压缩) | 26580 | 0% |
| json stringify with base64 | 25383 | 4.5% |
| pack gzip without base64 | 16903 | 36.41% |
| pack gzip with base64 | 8743 | 67.11% |
| pack gzip and compress with base64 | 9595 | 63.9% |
结论很明显:按键映射压缩 + gzip + Base64的组合拳,能把发送体积砍掉约64% ~ 67%,性能提升立竿见影。
九、小结:按键映射压缩算法的适用场景
最后划一下重点:
- ✅适合:键名长、字段重复多、数据量大(比如列表、表格数据)的 JSON;
- ⚠️注意:小对象反而可能因为附带
_映射表而变大,压缩率区间为 7.5% ~ 32.81%,用前建议先实测; - 🔧扩展:压缩后用
pack走 gzip + Base64 通道,是浏览器直传大数据的最优解。
这套按键映射压缩算法代码不过百行,却蕴含了"用映射表换取体积"的精妙思想。想亲手实验的话,直接运行项目里的 test/JSONC.js 测试用例,或打开 Benchmark 演示页 感受压缩前后的体积差异。看懂它,你就掌握了 JSON 瘦身的一把利器。
【免费下载链接】JSONCJSON compressor and decompressor项目地址: https://gitcode.com/gh_mirrors/json/JSONC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考