文章

手写 JSON 解析器

手写 JSON 解析器

实现原理

  • 理解JSON的语法,可以使用 https://www.json.cn/ 。按照 json 规范,除了对象,输入 Array、Boolean、Number、Undefined 都是可以的。
  • 绘制「对象」有限状态机:

    Untitled.png

    • 说明:先不用考虑状态机整体,先考虑最常用的对象输入
    • 对象状态机流程:
      • 首先判断 { 字符
      • 紧接着判断空白字符,判断其后是否为 }
        • 如果是,则结束
        • 如果不是,则解析 字符串 作为 key, 解析 空白字符,解析 : ,解析 value 的值。
        • 因为可以有多个 key value 对,所以继续解析 , 然后重复上一步

代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
   /**
     * 解析对象
     * @returns {object}
     */
    function parseObject() {
        if (str[idx] === '{') {
            ++idx;
            skipWhiteSpace(); // 跳过空白字符

            const res = {};
            let init = true;
            while (str[idx] !== '}') { // 循环匹配,找到多个 key-value
                if (!init) { // 如果不是第一个key-value,应该匹配 , 
                    skipWhiteSpace();
                    eatComma();
                    skipWhiteSpace();
                }
                init = false;

                const key = parseString(); // 解析 key
                skipWhiteSpace();
                eatColon(); // 匹配 : 
                skipWhiteSpace();
                const value = parseValue(); // 解析 value
                res[key] = value;
            }
            ++idx;
            return res;
        }
    }

		/**
     * 解析值
     * @returns {any}
     */
    function parseValue() {
        skipWhiteSpace(); // 跳过空白字符
				// 这里就是把 value 当作 string / number / object / array 等进行尝试匹配
        // 如果匹配成功,指针idx会移动,并且能拿到 val 
        const val = parseString() ?? parseNumber() ?? parseObject() ?? parseArray() ??
            parseKeyword("true", true) ?? parseKeyword("false", false) ?? parseKeyword("null", null) ?? parseKeyword("undefined", undefined);
        skipWhiteSpace(); // 跳过空白字符

        return val;
    }
  • 绘制「数组」有限状态机:

    Untitled.png

    • 说明:数组状态机简单一些
    • 流程:类似对象状态机。

代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
		/**
     * 解析数组
     * @returns {any[]}
     */
    function parseArray() {
        if (str[idx] === "[") {
            ++idx;
            skipWhiteSpace();

            const res = []
            let init = true;
            while (str[idx] !== ']') {
                if (!init) {
                    skipWhiteSpace();
                    eatComma();
                    skipWhiteSpace();
                }
                init = false;

                const value = parseValue();
                res.push(value);
            }
            ++idx;
            return res;
        }
    }
  • 依次绘制「其它数据类型」有限状态机,直接看代码即可,这里不重复了。其中比较麻烦的是 Number 。
  • 最终,由于前面提到的JSON规范,对象、Array数组等都可以直接作为Input进行解析,所以函数返回就是 parseValue 调用,而不是 parseObject

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      /**
       * @description 解析json
       * @author dongyuanxin
       * @param {string} str 
       */
      function parseJson(str) {
          let idx = 0;
          return parseValue(str);
              // ... 各种parse定义
      }
    
  • 使用效果

    Untitled.png

源码地址

bookmark

根据文章,自己也实现了一份JSON解析器代码。

参考链接

bookmark

bookmark

本文由作者按照 CC BY 4.0 进行授权