首页 » 漏洞 » 自己动手写 JSON 解析 & 格式化工具

自己动手写 JSON 解析 & 格式化工具

 

前段时间,老王在写项目代码的时候,要用到 JSON 。但是每次用的时候,因为都是压缩的,所以要去做一次格式化。到网上一搜在线 JSON 格式化工具,找到几个感觉还不错,不过因为我的 JSON 串很大,一贴进去,网页就卡死了。于是乎,作为程序员的老王,就萌发了写一个 JSON 解析和格式化的工具。咱说干就干,老王花了几天晚上,就写了这样一个。今天整好借着机会分享给大家 ~

这里先把地址发给大家,代码和效果地址: Simple Ma i n .com (老王自己去申请了一个域名,做了 ICP 备案,买了一个阿里云最便宜的服务器,现在把文章都放上去了。后面准备把自己开发中想用的工具都放上去 & 开源。现在没有做手机版兼容,所以大家用 PC 打开效果最好),有兴趣的同学可以敲下这个域名体验一下(订阅号不能贴链接, T_T )。

言归正传,做 JSON 格式化,大体上是分两步: JSON 的解析 + JSON 的格式化输出。

1 JSON 的解析:

这一步的主要工作,就是将一个字符串(比如: "[100,44.5]" )转化成内存中的数据结构(比如: array number 等)。这一步基本就是类似做编译原理里面的词法、语法、语意的分析。为了保证 JSON 解析的完整性,老王参照了 JSON 官方标准( http://json.org/ )来编写。不过,在此基础上做了一个升级。就是如果遇到错误,只要还有往下解析的可能,老王的程序就不会停留在错误的地方,而是会尽量解析完所有的字符串。最后将所有错误的地方,报告出来。类似现在流行的编译器。

自己动手写 JSON 解析 & 格式化工具

上图就显示了,在一个 JSON-Object 里面,遇到引号不规范、数字不规范的时候,解析器会尽量往下解析,而不是一遇到错误就停止。

好了,那我们具体来看看老王是如何来设计这个解析器的。

A 、字符串中获取有效字符。

老王先设计了一个 Symbol 类,这个类的目的,就是管理输入的字符串,并从中提取一个个有效的字符。我在这里偷了些懒,借用了 java.nio.CharBuffer 的一些功能。他有两个很重要的方法: next nextWithoutSpace ,用来获取下一个字符。为什么要有这两个方法呢,因为我们在解析的时候,类似 {"a" : "I love this game"} 冒号前后的空格符对于解析是没有意义的,而引号里面的空格是有意义的。所以获取的时候,要根据上下文的情况,来调用不同的函数获取相应的字符。

B 、解析

老王定义了一个主解析类: JsonParser 。他只是一个壳儿,用来接收用户的 JSON 字符串输入,然后调用可能的真正的解析类: ObjectParser ArrayParser 去尝试着解析。如果其中一个能解析,就返回;否则,尝试完所有可能以后,还是不能解析,就报错返回。

如果这个时候,字符串是以 "[" 作为第一个有效字符, ArrayParser 就开始工作。这个代码主要是四大段:

a 、检查是否是 "[" 开始;

b 、依次调用 ValueParser 去识别每一个 Value

c 、检查是否是 "," 分隔。如果是,则跳回到 b 重复执行;否则就跳出执行下一步;

d 、检查是否是 "]" 结束。

有兴趣的朋友可以详细看看这段代码,老王觉得写的还是比较清晰的。而 ValueParser 也是按照标准,用不同类型的 Parser 去尝试解析:

自己动手写 JSON 解析 & 格式化工具

自己动手写 JSON 解析 & 格式化工具

如果能解析成功,则直接返回;否则继续尝试下一个 Parser 直到尝试完毕。

如果一开始是以 "{" 这个字符开始的,则用 ObjectParser 来尝试解析。具体解析的方法和 ArrayParser 是类似的。

好了,通过以上的工作,我们最终就得到了一个 JsonElement 。他本来应该是一个接口,但是为了方便做格式化遍历的工作,老王把他设计成了一个抽象类,继承和实现了 Iterator 接口。他包含很多子类: JsonObject JsonArray JsonNumber JsonBoolean JsonString 等等。

每个元素都是通过对应的 Parser 识别产生出来的。他们的工作主要是用来表明类型,以及格式化输出的时候,做对应的自我展示。

好了,解析的过程大体就是这样。每一个类型的详细解释老王就不再赘述,具体要看的话,可以看看老王的源代码( simplemain.com )。

2 JSON 的格式化:

如果一切顺利,没有太大的问题,我们就将一个字符串转化成了内存里那个 JsonElement 根对象了(他可能包含更多的子对象:儿子、孙子、重孙子等等)。那我们格式化输出的时候,就是采用这种顺藤摸瓜的逻辑,从这个根开始,深度优先的遍历这棵树,输出这个 JsonElement

为了在不同场景下实现格式化,比如:在 shell cmd 的情况下,是需要输出纯文本的;在 web 的情况下,是要输出 html 的等等,老王抽象了一个 JsonFormatter 的接口,输入是 JsonElement 这个根元素,以及一个 PrintWriter (用于作为抽象的输出流)。另外,实现了一个 MetaFormatter ,这个类用来做原始格式化,他会将 JsonElement 组织成一行一行的,每行多少个缩进、有多少个异常等等 Meta 信息都会被提取出来。

最后我们做逻辑实现的时候,只需要调用这个 MetaFormatter 就可以轻松实现我们大多数场景下的格式化需求。

自己动手写 JSON 解析 & 格式化工具

上面这个就是老王实现的一个纯文本的格式化类 TextJsonFormatter 。在 SimpleMain.com 的网页上,老王也是从简单的调用 MetaFormatter 来实现了 Html 的生成。

MetaFormatter 的实现,比较麻烦的一点就是深度优先遍历 JsonElement 树。为了让代码写起来好看一些,老王用了 Iterator 接口,将深度遍历变成了一个递归迭代的过程,每个 JsonElement 实体对象负责做自己的 Iterator ,这样就从代码层面上尽量降低了耦合。有兴趣的同学可以看看这一部分的代码,还是有些意思。

另外,最近很流行 emoji 表情符,老王也在代码里对实体字符做了转义,支持了 emoji 表情的展示,感觉还是很有意思的,哈哈哈 ~

自己动手写 JSON 解析 & 格式化工具

自己动手写 JSON 解析 & 格式化工具

以上就是一个调用的代码和纯文本的 JSON 格式化效果,输出中包含了行号、错误的行、错误的信息等等。大家有兴趣的话,就去下载代码试试吧。

因为代码是开源的,大家可以尽情的去改。如果发现 bug ,可以提给老王哈 ~ 对应的地址大家别忘了: SimpleMain.com

自己动手写 JSON 解析 & 格式化工具

原文链接:自己动手写 JSON 解析 & 格式化工具,转载请注明来源!

0