先恭喜热火没有在3-0的情况下被凯尔特人翻盘,抢七获胜成功晋级总决赛~
最近的项目用到了fastjson,因为源码比较容易搞到,所以就拿来简单的了解了一下,json的主要功能就是解析json和生成json字符串,今天主要是从解析json的方向先来入手。
fastjson里面的核心解析器是:DefaultJSONParser,其主要作用就是将JSON格式的字符串解析为java对象。DefaultJSONParser的解析算法基于递归下降算法。
本文主要就是详细了解一下这个递归下降算法。
目录
递归下降算法
产生式
相关概念
fastjson中的递归下降
DefaultJSONParser源码
初始状态:读取到 '{',进入parseObject方法
parseObject方法:创建JSONObject对象,读取到 "name":"Tom"
读取到 "address": {,递归到parseObject方法
parseObject方法:创建JSONObject对象,读取到 "province": "Guangdong",}
读取到 '}',JSON解析完成。
总结
递归下降算法
递归下降算法是一种基于产生式的自顶向下的语法分析方法,即从文法的起始符号开始,按照产生式展开,直到匹配完整个输入串。
产生式
产生式是一种知识表达方法,是事实与规则的表示,如:“老李年龄是35岁”,可以写成产生式:
{
"name":"老李",
"age":35
}
相关概念
递归下降算法的主要思想是,将文法的每个非终结符看作一个函数,每个函数对应一个产生式,函数的任务是把输入串匹配到该产生式的右部。在函数内部,可以再次调用其他函数,以实现对子表达式的递归分析。如:
<trem> ::= <expr>+<term> | <expr>-<term> | <term> <expr> ::= x
被<>括起来的就是非终结符,因为其可以被::=右侧的式子代替。
“|”表示选择,意思就是<term>可以被<expr>+<term>代替,也可以被<expr>-<term>代替,还可以被<term>代替。
没有出现在::=左侧的就被称作终结符,比如“x”,“+”,“-”。
递归下降算法通常需要先对文法进行一些处理,例如,将左递归的产生式转化为右递归的产生式。
如果语法中有产生式,A→A| … 称为左递归产生式。因为每一次解析A的时候,会碰到A可以代替A的情况,这样就会无限的递归下去。AAAAAAAAAAAAA
fastjson中不需要担心,因为json文法属于LL(1)文法,
LL(1)文法的解释是:
L:自顶向下分析是从左向右扫描输入串。
L:分析过程中将用到最左推导。
1:表明只需要向右看一个符号便可决定如何推导。
所以json里面不会出现左递归的情况。
fastjson中的递归下降
当DefaultJSONParser接收到一个JSON字符串时,它首先会将其转化为一个Token流。例如,对于以下JSON字符串:
{ "name": "Tom", "address": { "province": "Guangdong", } }
DefaultJSONParser会将其转化为如下的Token流:
{, name, :, Tom, ,, address, :, {, province, :, Guangdong, , , }, }
接下来,DefaultJSONParser会根据Token流的类型,逐一解析出相应的Java对象。例如,在上述JSON字符串中,DefaultJSONParser会先解析出一个JSONObject,然后再解析出其中的每个键值对。
DefaultJSONParser源码
主要看的是fastjson中的DefaultJSONParser的parse方法,方法签名如下所示:
public Object parse()
该方法会调用另外一个重载方法public Object parse(Object fieldName)
该方法的主要代码如下所示:
public Object parse(Object fieldName) {
final JSONLexer lexer = this.lexer;
switch (lexer.token()) {
//中间基本都是case,为了不影响篇幅,先省略
... ...
}
}
用一个例子来讲一下源代码,现在有一个json串是这样的:
{
"name": "Tom",
"address": {
"province": "Guangdong",
}
}
1.初始状态:读取到 '{',进入parseObject方法
//上面那个LBRACE是JSONToken中的常量12,代表的就是符号"{",是大部分json字符串的初始字符。
//public final static int LBRACE = 12; // ("{"),
case LBRACE:
Map object = isEnabled(Feature.UseNativeJavaObject)
? lexer.isEnabled(Feature.OrderedField)
? new HashMap()
: new LinkedHashMap()
: new JSONObject(lexer.isEnabled(Feature.OrderedField));
return parseObject(object, fieldName);
//在fastjson中,UseNativeJavaObject是一个配置选项,
//用于在JSON与Java对象之间进行转换时控制是否使用Java原生对象。
//也就是使用HashMap代替JSONObject,使用ArrayList代替JSONArray
//默认是false
isEnabled(Feature.UseNativeJavaObject) 没有配置的情况下会是false
//属性是否是有序的
//默认是false,也就是JSONObject里面是一个HashMap
//如果是ture的话,里面会是一个LinkedHashMap
lexer.isEnabled(Feature.OrderedField)
//最后获取一个JSONObject传入了parseObject方法里面
parseObject(object, fieldName);
2.parseObject方法:创建JSONObject对象,读取到 "name":"Tom"
public final Object parseObject(final Map object, Object fieldName) {
final JSONLexer lexer = this.lexer;
//判断token是否为空,咱们目前代码,token是符号“{”
if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
return null;
}
//判断符号是否是“}”
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
}
//判断是否是String字符串,并且长度是0
if (lexer.token() == JSONToken.LITERAL_STRING && lexer.stringVal().length() == 0) {
lexer.nextToken();
return object;
}
//判断token不是“{”,也不是“,”
if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error, expect {, actual " + lexer.tokenName() + ", " + lexer.info());
}
//解析时的上下文对象
ParseContext context = this.context;
try {
//通过第一步知道,这个表达式结果为true
boolean isJsonObjectMap = object instanceof JSONObject;
//最终的map
Map map = isJsonObjectMap ? ((JSONObject) object).getInnerMap() : object;
boolean setContextFlag = false;
for (;;) {
//跳过字符串的空格换行等空白部分
lexer.skipWhitespace();
//获取当前的字符,当前字符应该是“"”
char ch = lexer.getCurrent();
//这一段代码时去掉多余的“,”号,比如{"a":1,,,"b":2}中间有两个逗号是多余的
//但是目前咱们的符号是“{”,所以在这一步会跳过该段代码
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (ch == ',') {
lexer.next();
lexer.skipWhitespace();
ch = lexer.getCurrent();
}
}
//接下来这一段是获取key值的
boolean isObjectKey = false;
Object key;
if (ch == '"') {
//扫描key值,获取到key值“name”
key = lexer.scanSymbol(symbolTable, '"');
//跳过空格
lexer.skipWhitespace();
//当前的字符应该是":"
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", name " + key);
}
}
... ...
//获取到下一个token
if (!isObjectKey) {
lexer.next();
lexer.skipWhitespace();
}
//获取当前字符串,是“"”
ch = lexer.getCurrent();
lexer.resetStringPosition();
... ...
//设置上下文,目前为止上下文应该还是null,啥都没有
if (!setContextFlag) {
if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
context = this.context;
} else {
//上下文会有一个初始的上下文,里面没有元素
ParseContext contextR = setContext(object, fieldName);
if (context == null) {
context = contextR;
}
setContextFlag = true;
}
}
......
Object value;
if (ch == '"') {
//扫描字符串,获取value值“Tom”
lexer.scanString();
String strValue = lexer.stringVal();
value = strValue;
... ...
//将key值和value值插入进map
map.put(key, value);
}
... ...
//获取到下逗号,进行下一次循环获取下一个映射
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch == ',') {
lexer.next();
continue;
}
3.读取到 "address": {,递归到parseObject方法
这一步在获取value值之前,和上一步都是一样的。获取value值的分支如下:
else if (ch == '{') {
//获取到下一个token
lexer.nextToken();
//上一层是否是数组,fieldName如果为null说明没有上一层
final boolean parentIsArray =
fieldName != null && fieldName.getClass() == Integer.class;
Map input;
//这个特性是对map的反序列化进行个性定制,默认就是false
//将input初始化成JSONObject
if (lexer.isEnabled(Feature.CustomMapDeserializer)) {
... ...
} else {
input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
}
//开始设置上下文
ParseContext ctxLocal = null;
//将目前的key值设置到上下文里面
if (!parentIsArray) {
ctxLocal = setContext(this.context, input, key);
}
//开始解析value值
Object obj = null;
//value值已经解析完成,初始化为false
boolean objParsed = false;
... ...
//递归调用parseObject
if (!objParsed) {
obj = this.parseObject(input, key);
}
... ...
}
4.parseObject方法:创建JSONObject对象,读取到 "province": "Guangdong",}
和第二步基本一样,只不过在获取完value之后,有些不同
//第二步会进入到if里面
if (ch == ',') {
lexer.next();
continue;
}
//这一步会进入到这里面
else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken();
this.setContext(value, key);
//获取到了{"province": "Guangdong"},返回到上一层
return object;
}
5.读取到 '}',JSON解析完成。
这一步承接第三部分的源代码
//接上段
if (!objParsed) {
obj = this.parseObject(input, key);
}
......
map.put(key, obj);
//parentIsArray是false
if (parentIsArray) {
setContext(obj, key);
}
//会进入到这个条件分支中去
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
setContext(context);
return object;
}
总结
今天主要是了解了什么是递归下降算法,以及fastjson在解析一个json时的简单步骤,更加复杂的情况在之后对源码更加了解之后,可能会详细了解。文章中出现很多fastjson的类,之后会慢慢的了解,比如Feature特征,Token,JSONLexer,SymbolTable等等。希望你能在这篇文章里获得到那么一丁点的知识。