介绍

后面两节咱们介绍了分词和分词荡涤,通过荡涤后的token列表曾经能够用来生产语法树,本节咱们将实现语法树的生成,语法树的目标就是将数据结构化,什么叫结构化,比方字符串表达式(1+(2*3))要写个程序计算这个后果还是有点难的,因为这个字符串只是一个字符串,咱们须要去解析,那么这个字符串就不是一个结构化的数据,如果给你的是解析后的数据,那么就很好解决,解析后的数据就是结构化的数据,你可能会说那不就是分词吗,其实不是的,也是以下面那个表达式为例,分词都是平铺构造,没有层级关系,但表达式有优先级,嵌套等层级关系,这个就是语法树要做的事。

实现

难点

生成语法树的难点在于,token数组是平铺构造,但语法树是树形构造,有层级关系,咱们须要将将平铺构造变成树形构造,当token数组中蕴含object和array时语法树就会有新的分支,因而重点就是要解决好object和array,这两个解决形式实质上是统一的,能够简化为找括号问题,什么意思呢,比方一个表达式(1+(2+(3+4)+5)+6)如何找到成对的括号,一种简略的办法是如果碰到(就进入递归,直到碰到)退出,这样最初一次递归会先匹配第一个)和咱们想要的后果相符,有点相似栈,后进先出,就以(1+(2+(3+4)+5)+6)为例,总共17个字符,括号的匹配状况应该是

0匹配163匹配136匹配10

代码实现

public static void test(String text) {        int begin = 0;        int end = 0;        while (index < text.length()) {            int c = text.charAt(index);            if (c == '(') {                begin = index;                char cc = text.charAt(++index);                while (cc != ')') {                    test(text);                    cc = text.charAt(index);                }                end = index;                System.out.println(String.format("%d匹配%d", begin, end));            } else if (c == ')') {                break;            }            ++index;        }    }

输入

6匹配103匹配130匹配16

因为是最外面括号开始匹配所以先输入6匹配10,后果是合乎预期的,了解了这个就很容易了解语法树的生成过程

实现

首先定义形象语法树数据结构,如下

public class Ast {    //节点类型    private String type;    //如果是object则是字段列表    //如果是array则是值列表    private List<Ast> items;    //字段名(可为空)    private String name;    //字段值    private Object value;  ....}

语法树构建步骤如下

  • 如果是object和array,那么就依照递归的形式进行遍历
  • 如果是key,那么依据value的类型进行解决,如果是object和array,那么依照递归的形式进行遍历,如果是根本类型,间接解决
  • 如果是value,间接解决

代码如下:

/** * 生成形象语法树 * * @return */public List<Ast> generateAST() {    List<Ast> items = new ArrayList<>();    //currentIndex作为以后读取token的地位    //取出对象则currentIndex须要往前挪动    Token token = jsonTokens.get(currentIndex);    if ("object".equals(token.getType()) && "{".equals(token.getValue())) {        Ast item = new Ast();        item.setType(token.getType());        //如果json语法错误currentIndex有可能超过范畴,这里先不思考语法错误问题        Token tt = jsonTokens.get(++currentIndex);        while (!"object".equals(tt.getType()) || ("object".equals(tt.getType()) && !"}".equals(tt.getValue()))) {            item.getItems().addAll(generateAST());            tt = jsonTokens.get(currentIndex);        }        items.add(item);        ++currentIndex;    } else if ("array".equals(token.getType()) && "[".equals(token.getValue())) {        Ast item = new Ast();        item.setType(token.getType());        Token tt = jsonTokens.get(++currentIndex);        while (!"array".equals(tt.getType()) || ("array".equals(tt.getType()) && !"]".equals(tt.getValue()))) {            item.getItems().addAll(generateAST());            tt = jsonTokens.get(currentIndex);        }        items.add(item);        ++currentIndex;    } else if ("key".equals(token.getType())) {        // key和value是成对呈现,如果只有key没有value阐明json语法错误,这里临时不思考语法错误        Token value = jsonTokens.get(++currentIndex);        if ("object".equals(value.getType()) || "array".equals(value.getType())) {            //对象和数组须要递归获取语法树            List<Ast> tts = generateAST();            //如果是key-value构造必须设置key的名称            tts.get(0).setName((String) token.getValue());            return tts;        } else if ("value".equals(value.getType())) {            Ast item = new Ast();            item.setValue(value.getValue());            item.setType(value.getType());            item.setName((String) token.getValue());            items.add(item);            ++currentIndex;        }    } else if ("value".equals(token.getType())) {        //只有value没有key的状况,比方["1",2,true,null]        Ast item = new Ast();        item.setValue(token.getValue());        item.setType(token.getType());        items.add(item);        ++currentIndex;    }    return items;}

代码看着挺长,但就是做下面说的三件事,外面的逻辑并不简单

测试

语法树结构不容易打印进去,咱们用fastjson转化为json进行打印

public class Main {    public static void main(String[] args) throws Exception {        InputStream fin = Main.class.getResourceAsStream("/example.json");        byte[] buf = new byte[fin.available()];        fin.read(buf);        fin.close();        String json = new String(buf, "utf-8");        JSONParser parser = new JSONParser();        List<Token> tokens = parser.tokenizer(json);        tokens = parser.tokenClean(tokens);        List<Ast> astItems = parser.generateAST();        Ast ast = astItems.get(0);        System.out.println(String.format("|%-12s|%-12s|%-15s|", "type", "valueType", "value"));        System.out.println("-------------------------------------------");        for (Token t : tokens) {            System.out.println(String.format("|%-12s|%-12s|%-15s|",                    t.getType(),                    t.getValueType(),                    t.getValue()));        }        System.out.println("-------------------------------------------");        System.out.println(JSON.toJSONString(ast, true));    }}

后果:

{    "items":[        {            "name":"name",            "type":"value",            "value":"asan"        },        {            "name":"age",            "type":"value",            "value":32        },        {            "name":"married",            "type":"value",            "value":true        },        {            "name":"birthday",            "type":"value",            "value":"1992-02-08"        },        {            "name":"salary",            "type":"value",            "value":1234.56        },        {            "name":"description",            "type":"value",            "value":"a \"hudsom\" man"        },        {            "items":[                {                    "type":"value",                    "value":"coder"                },                {                    "type":"value",                    "value":"mid-age"                }            ],            "name":"tags",            "type":"array"        },        {            "items":[                {                    "name":"province",                    "type":"value",                    "value":"福建省"                },                {                    "name":"city",                    "type":"value",                    "value":"泉州市"                },                {                    "name":"area",                    "type":"value",                    "value":"晋江市"                }            ],            "name":"location",            "type":"object"        },        {            "items":[                {                    "items":[                        {                            "name":"relation",                            "type":"value",                            "value":"couple"                        },                        {                            "name":"name",                            "type":"value",                            "value":"Helen"                        }                    ],                    "type":"object"                },                {                    "items":[                        {                            "name":"relation",                            "type":"value",                            "value":"daughter"                        },                        {                            "name":"name",                            "type":"value",                            "value":"XiaoMan"                        }                    ],                    "type":"object"                }            ],            "name":"family",            "type":"array"        }    ],    "type":"object"}

这样咱们胜利构建了一棵语法树

代码

残缺代码请参考我的项目https://github.com/wls1036/tiny-json-parser的0x03分支