JSON格式化

我个人主要是做一些后端的工作,比如php、python、c之类的,c比较少,最主要的是php,不过我非常喜欢js,所以经常会愿意去写一些小玩意自娱自乐。

今天在测试接口的时候,使用原生js的XMLHttpRequest去请求,直接使用document….innerHTML输出到页面,因为没有浏览器的json格式化没有生效,所以接口响应的json数据就看起来很不舒服。

于是乎,我就想起了为啥我自己不能实现一个,应该不是很难吧。

我仔细思考了一下,这玩意不就是遇到大括号、中括号和中括号就换行吗,每行还有个缩进,人家再高级一点的还有颜色,折叠功能、缩进对齐线之类的,于是我觉得一步一步来,先把格式化和颜色实现出来,后面的折叠、动画和缩进对齐线再慢慢做。

电影有句话说的好啊:“步子迈大了,咔,容易扯着蛋”。

扯了这老些没用的,下面开始我的正经思路:

合法的JSON字符串是一个单行字符串,边界符号是“{} []”,键值对之间是以英文逗号“,”作为分隔,键和值之间是用英文冒号“:”进行分隔。JSON内的字符串必须以双引号包括在外侧,数字类型或布尔类型可以不使用双引号包括。逗号分隔符的后面必须至少存在一个键值对(即末尾的键值对的后面不能有逗号分隔符了,这里的逗号指的是键值对分隔符,而不是指值内的逗号字符串)。

看看别人优秀的是什么样子的。

从某平台的json格式化服务截取

可以发现在“{, [”的后面都会有一个换行,每个键值对的后面都有一个换行,在符号“], 和 }, ”的后面都会有一个换行。

而且格式化后的JSON是有“结构区块”的,从缩进来区分不同的结构块,这一点有点像python,不过这有点牵强,格式化这样做的目的是为了能够很清晰的查看JSON的结构,与python的目的是有本质的区别的。

从截图来看,只要遇到一个“{ 或 [”,就要出现缩进,而且可以看出,缩进是随着遇到的个数增加的,这是成正比啊。而且只要遇到“]或}”,缩进就要少一个。

说到这里是不是就有感觉了,缩进就是在匹配括号啊,这匹配括号在逆波兰式里的操作啊,不就是基础的数据结构“栈”吗。

那我对整个字符串进行遍历判断是不就能做到了,那有了思路就可以动手了,能动手我就不在这里扯?了。

<!-- 这里在页面放一个pre标签,让输入的结构按照我们需要的形式展现 -->
<pre id="json"></pre>

假设我有一个jsonStr,我想要格式化它。

var jsonStr = '{"state":{"code":0,"success":true,"ok":1},"body":[{"count":2394,"dbName":"star_all"},{"count":133,"dbName":"star"},{"count":7,"dbName":"zy"},{"count":1,"dbName":"gordon_test"}]}';
function format(str){
    var stack = []; //栈-用于括号匹配
    var tmpStr = '';    //新格式化JSON字符串
    var len = str.length;   //原始JSON长度

    //遍历每一个字符
    for (let i = 0; i < len; i++) {

        //当遇到结构块起始结构
        if (str[i] == '{' || str[i] === '[') {

            //起始结构后面直接换行  
            tmpStr += str[i] + "\n";

            //入栈
            stack.push(str[i]);
            
            //这里的意思是结构块起始的下一行开始就会有一个缩进,缩进量与遇到的结构块起始符个数成正比1:1
            tmpStr += "\t".repeat(stack.length);
        } 
        //当遇到结构块结束符
        else if (str[i] == ']' || str[i] === '}') {

            //因为本身JSON格式是固定的,所以括号一定是成对的,这里先不考虑错误的json数据
            //遇到结束符就退栈,
            stack.pop();

            //结束符本身输出到下一行,并减少一个缩进
            tmpStr += "\n"+"\t".repeat(stack.length) + str[i];
        } 
        //当遇到逗号的时候
        else if (str[i] == ',') {
            //逗号后方直接换行,以及下一行的缩进处理
            tmpStr += str[i] + "\n" + "\t".repeat(stack.length);
        } 
        else {
            //其他字符直接复制
            tmpStr += str[i];
        }
    }
    return tmpStr;
}

返回的数据放到<pre>标签内

document.querySelector('#json').innerHTML = format(jsonStr);

输出的效果如下图

制表符\t稍微有点远,当然使用4个 也可以,别纠结~

这黑白色的不美观,那就给上个色呗,看看人家的,括号一个颜色,键值对一个颜色,值一个颜色,我不能抄它的,我觉得字符串,数字、布尔都分别用一种颜色就行,这个实现就都能实现了。

那既然需要分开使用不同的颜色,那么必然就涉及CSS了,每个结构就得有HTML结构了,我直接用正则是不是就解决了。

先写个CSS样式把

.bold{
    font-weight: 900;
}
.string-color {
    color: darkred;
}
.token {
    color: darkgreen;
}
.number {
    color: green;
}
.bool {
    color: orange;
}
//使用捕获,匹配全部的边界符号,class使用token
tmpStr = tmpStr.replace(/([\{\[\]\}])/g, '<span class="token bold">$1</span>');

//使用零宽断言和捕获,匹配全部的两侧是双引号的字符串,class使用string
tmpStr = tmpStr.replace(/(?<=\")(\w+)(?=\")/g, '<span class="string bold">$1</span>');

//使用零宽断言,匹配全部的前面位置是冒号,后面是逗号或换行的数字类型值
tmpStr = tmpStr.replace(/(?<=\:)(\d+)(?=[\,\n])/g, '<span class="number bold">$1</span>');

//同理匹配布尔,
tmpStr = tmpStr.replace(/(?<=\:)(true|false)(?=[\,\n])/g, '<span class="bool bold">$1</span>');

零宽断言就是匹配一个位置,分负向零宽断言和正向零宽断言,不知道的可以搜索一下。

效果还行

到这里就初步完成了一个还算能入眼的基础JSON格式化小方法。后续再加个闪电爆炸的特效,下次再分享~

本文分享自微信公众号 - 可回收BUG(way-of-full-stack)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券