JSON Parser

Table of Contents


需求

实现一个 JSON 解析的工具。

什么是 JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。

JSON 的形式

对象 是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始, “}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。

20170627_095123_373180Dm.png

Figure 1: object

数组 是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。

20170627_095153_37318BOs.png

Figure 2: array

(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、null、对象(object)或者数组(array)。这些结构可以嵌套。

20170627_095226_37318OYy.png

Figure 3: value

字符串(string)是由双引号包围的任意数量 Unicode 字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。

字符串(string)与 C 或者 Java 的字符串非常相似。

20170627_095650_37318AiB.png

Figure 4: string

数值 (number)也与 C 或者 Java 的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

20170627_095728_37318NsH.png

Figure 5: number

如何做

上面我们说了什么是 JSON。下面我们先用一种简单的方式来实现 JSON 的解析,ANTLR。

使用 ANTLR 我们只需要写一下相应的语法规则就可以了,

关于如何安装只说下 OS X 系统下的,其他参考这里

cd /usr/local/lib
sudo curl -O http://www.antlr.org/download/antlr-4.7-complete.jar
export CLASSPATH=".:/usr/local/lib/antlr-4.7-complete.jar:$CLASSPATH"
alias antlr4='java -jar /usr/local/lib/antlr-4.7-complete.jar'
alias grun='java org.antlr.v4.gui.TestRig'

下面是定义的规则 JSON.g4:

/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */

// Derived from http://json.org
grammar JSON;

json
   : value
   ;

obj
   : '{' pair (',' pair)* '}'
   | '{' '}'
   ;

pair
   : STRING ':' value
   ;

array
   : '[' value (',' value)* ']'
   | '[' ']'
   ;

value
   : STRING
   | NUMBER
   | obj
   | array
   | 'true'
   | 'false'
   | 'null'
   ;


STRING
   : '"' (ESC | ~ ["\\])* '"'
   ;
fragment ESC
   : '\\' (["\\/bfnrt] | UNICODE)
   ;
fragment UNICODE
   : 'u' HEX HEX HEX HEX
   ;
fragment HEX
   : [0-9a-fA-F]
   ;
NUMBER
   : '-'? INT '.' [0-9] + EXP? | '-'? INT EXP | '-'? INT
   ;
fragment INT
   : '0' | [1-9] [0-9]*
   ;
// no leading zeros
fragment EXP
   : [Ee] [+\-]? INT
   ;
// \- since - means "range" inside [...]
WS
   : [ \t\n\r] + -> skip
   ;

我们再写一个测试文件:

import java.io.File;
import java.util.Scanner;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

public class JSONParserTest
{

    public static void main(String[] args) throws Exception
    {
        String content = new Scanner(new File("jsonExample.txt")).useDelimiter("\\Z").next();
        System.out.println( "JSON File:\n" + content + "\n\n");

        ANTLRInputStream input = new ANTLRInputStream( content );

        JSONLexer lexer = new JSONLexer(input);

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        JSONParser parser = new JSONParser(tokens);

        ParseTree tree = parser.json();

        System.out.println( "ParseTree:\n" + tree.toStringTree( parser ) + "\n");
    }
}

另外 jsonExample.txt

{
  "student":
  {
    "id" : "12345678",
    "prename" : "John",
    "surname" : "Doe",
        "address" :
        {
          "street" : "Johndoestreet",
          "postcode" : "99999"
        },
    "email"   : "johndoe@doe.com"
  }
}

首先

antlr4 JSON.g4
javac JSON*.java JSONParserTest.java
java JSONParserTest

就会打印出解析树:

(json (value (obj { (pair "student" : (value (obj { (pair "id" : (value "12345678")) , (pair "prename" : (value "John")) , (pair "surname" : (value "Doe")) , (pair "address" : (value (obj { (pair "street" : (value "Johndoestreet")) , (pair "postcode" : (value "99999")) }))) , (pair "email" : (value "johndoe@doe.com")) }))) })))

另一种实现方式

这里介绍一种 Python 的实现方式。这个版本只是一个 Demo, 很多小的情况没有覆盖到。 大部分常见的 JSON 结构应该都能解析。 代码部分在 GitHub 上。主要是 scanner 和 decoder 这两个文件。 参考上面 JSON 的几种状态,我们根据状态机很容易就能写出 JSON parser。