开发个 json 格式化工具 兼容常见异常格式(不带引号的key 末尾多余逗号)
背景
我经常需要格式化json,有时候是需要把它变得易读,有时候是需要把它压缩。
我常用网上的在线工具,但是有些工具很卡、有些工具有广告、有些工具甚至有泄露数据的安全风险。所以我自己动手开发了一个工具。
而且,我只想用纯 html js css 实现,不依赖任何库,尽可能减少它的体积。
理想效果
3种格式化模式
考虑到我的日常使用主要是2种场景:
- 易读,易修改。
- 压缩json(删除多余换行符和空格)。
所以我放了单选按钮在开头。包括3个:
- 2空格,基本类型数组不换行
- 2空格,基本类型数组和基本类型对象不换行
- 不换行,不空格
2空格,基本类型数组不换行
其中第一个是默认选项,因为「易读」确实是最常见的诉求。而且我经常遇到这种数据:
{
"a": [
1,
2
],
"b": [
2,
3
]
}
这样的话,数组会显得非常「不易读」。数组明明一行就可以展示完,却换了很多行。
所以,我在默认选项中设置了「基本类型数组不换行」。
2空格,基本类型数组和基本类型对象不换行
此外,这种格式也很常见:
{
"a": {"x": 1, "y": 2},
"b": {"x": 3, "y": 34}
}
如果每个x
和每个y
都换行,其实也不太方便观察。所以我第二个选项设置了「基本类型数组和基本类型对象不换行」。此时这种情况的表现会更直观。
不换行,不空格
另外,如果你希望删除多余换行符和空格,可以用第三个选项。
格式容错
我们输入json时,很多时候容易有些失误,包括:
- key 没带引号
- key 带的是单引号
- 最后一个项目多了个逗号
这些都会导致 json 格式非法。但其实他们完全可以被正确解析。所以我希望兼容这些错误。
使用地址 & 源码
使用地址:tool.hullqin.cn/json-format…
开发难点
- 如何兼容错误。
- 如何实现「基本类型的数组不换行」。
兼容错误
我采用了简单的方案:eval
,直接把它当作JS对象来执行。其实json数据本身也可以直接当做JS语法来执行。因此用eval
既可以处理合法json、也可以处理js对象。非常完美的兼容了json格式错误。
核心逻辑如下:
let data;
eval('data = ' + event.target.value.trim());
如何实现:基本类型数组不换行、基本类型对象不换行
如果直接使用JSON.stringify
,像这样:
JSON.stringify(data, null, 2);
那么数组所有项目都会被换行,所以不能这么简单写。
我们需要手写一个stringify
,但是针对基本类型数组或对象,特殊处理。我写完了,如下:
const getLevelSpaces = (level) => Array(level * 2).fill(' ').join('');
const isBasicType = (item) => item === null || typeof item !== 'object';
const stringify = (obj, skipBasicObj, level = 0) => {
if (obj === null || obj === undefined) return 'null';
if (typeof obj !== 'object') return obj.toString();
if (Array.isArray(obj)) {
if (obj.every(item => isBasicType(item))) return '[' + obj.join(', ') + ']';
return '[\n' + getLevelSpaces(level + 1) + obj.map(item => stringify(item, skipBasicObj, level + 1)).join(',\n' + getLevelSpaces(level + 1)) + '\n' + getLevelSpaces(level) + ']';
}
const entries = Object.entries(obj).filter(value => value[1] !== undefined);
if (entries.length === 0) return '{}';
if (skipBasicObj && entries.every(value => isBasicType(value[1]))) {
return '{' + entries.map(value => '"' + value[0] + '": ' + stringify(value[1], skipBasicObj)).join(', ') + '}';
}
return '{\n' + getLevelSpaces(level + 1) + entries.map(value => '"' + value[0] + '": ' + stringify(value[1], skipBasicObj, level + 1)).join(',\n' + getLevelSpaces(level + 1)) + '\n' + getLevelSpaces(level) + '}';
};
写在最后
我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋、象棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》、《极致用户体验》。
转载自:https://juejin.cn/post/7159222601707946015