news 2026/8/21 16:00:03

深入JSONC源码:按键映射压缩算法原理逐行解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入JSONC源码:按键映射压缩算法原理逐行解析

深入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.decompressJSONC.pack/JSONC.unpack两组 API 组成:

  • 按键映射压缩compress用短键替换长键,压缩率可达7.5% ~ 32.81%
  • Gzip 深度压缩pack在此基础上叠加 gzip + Base64,让数据体积再降一档。

整个库零依赖、源码只有三百多行,非常适合浏览器端直传数据、接口瘦身等场景。项目还提供了 Benchmark 演示目录 与 PHP 端解压实现,方便前后端配合使用。

二、核心思路:按键映射压缩算法究竟做了什么?

看代码前先建立直觉。假设原始 JSON 长这样:

{ "userName": "Tom", "userEmail": "tom@x.com" }

userNameuserEmail各 8~10 个字符,但字段名在大量数据里会重复出现千百次。按键映射压缩算法的思路很直白:

  1. 遍历整个 JSON,收集所有不重复的键名;
  2. 给每个键名分配一个超短代号ABC…);
  3. 用代号替换原键名,同时把对照关系存进一个_属性里;
  4. 解压时读取_,把代号反向替换回原名即可。

压缩后的结果类似{ _: { 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 码数组,再由_getSpecialKeyString.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索引不参与映射,避免无意义开销;
  • 去重:借助uniquecontains保证同一键名只分配一次代号;
  • 静态计数器_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(不压缩)265800%
json stringify with base64253834.5%
pack gzip without base641690336.41%
pack gzip with base64874367.11%
pack gzip and compress with base64959563.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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/21 15:59:37

多层斯塔克尔伯格博弈:异质性领导者与非追随者智能体的实战建模

1. 从“猫鼠游戏”到“三国演义”&#xff1a;非标准斯塔克尔伯格博弈的实战场景 在传统的博弈论模型里&#xff0c;斯塔克尔伯格&#xff08;Stackelberg&#xff09;博弈常常被比作一个“领导者-追随者”的猫鼠游戏。领导者先动&#xff0c;制定策略&#xff08;比如定价、产…

作者头像 李华
网站建设 2026/8/21 15:57:41

5步搭建团队知识库:科亿知识库全文检索与管理实战指南

5步搭建团队知识库&#xff1a;科亿知识库全文检索与管理实战指南 【免费下载链接】-kykms 科亿知识库 KY KMS 是一款基于Elasticsearch的文档型知识库管理系统&#xff0c;提供强大的全文检索与文档分类管理功能 项目地址: https://gitcode.com/gh_mirrors/ky/-kykms 周…

作者头像 李华
网站建设 2026/8/21 15:53:45

Kiwix Swift 6迁移实践:全局Actor与并发安全在开源App中的应用

Kiwix Swift 6迁移实践&#xff1a;全局Actor与并发安全在开源App中的应用 【免费下载链接】apple Kiwix for iOS, iPadOS & macOS 项目地址: https://gitcode.com/gh_mirrors/ap/apple Kiwix 是一款知名的开源离线阅读器&#xff0c;覆盖 iOS、iPadOS 与 macOS 三大…

作者头像 李华
网站建设 2026/8/21 15:52:17

基于SpringBoot的校园周边美食探索分享系统(源码+讲解视频+LW)

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/8/21 15:51:21

AI智能体在开源项目中的贡献模式与代码变更趋势分析

1. 项目概述&#xff1a;当AI智能体成为“野生”开发者最近两年&#xff0c;如果你是一名开发者&#xff0c;几乎不可能没听说过GitHub Copilot或者OpenAI Codex。它们从最初的代码补全工具&#xff0c;逐渐演变成了能够理解复杂指令、生成完整函数甚至模块的“AI结对编程伙伴”…

作者头像 李华
网站建设 2026/8/21 15:51:17

数学建模必备:数据插值与曲线拟合的核心原理与Python实战

1. 从“插值”到“拟合”&#xff1a;两种核心思路的实战分野 在数学建模的赛场上&#xff0c;数据往往不是完美的。我们拿到的可能是一组稀疏的观测点&#xff0c;或者是一堆带有噪声的实验数据。这时候&#xff0c;如何从有限的数据中“读出”更多信息&#xff0c;或者提炼出…

作者头像 李华