2021SC@SDUSC
上篇我们分析了MapSerializer和JavaBeanSerializer,这篇我们来分析token;
JSONToken成员:
com.alibaba.fastjson.parser.JSONToken定义了fastjson需要的token标识符:
/** 1 关联到 error */
public final static int ERROR = 1;
/** 2 关联到 int */
public final static int LITERAL_INT = 2;
/** 3 关联到 float */
public final static int LITERAL_FLOAT = 3;
/** 4 关联到 string */
public final static int LITERAL_STRING = 4;
/** 5 关联到 iso8601 */
public final static int LITERAL_ISO8601_DATE = 5;
/** 6 关联到 true */
public final static int TRUE = 6;
/** 7 关联到 false */
public final static int FALSE = 7;
/** 8 关联到 null */
public final static int NULL = 8;
/** 9 关联到 new */
public final static int NEW = 9;
/** 10 关联到 ( */
public final static int LPAREN = 10;
/** 11 关联到 ) */
public final static int RPAREN = 11;
/** 12 关联到 { */
public final static int LBRACE = 12;
/** 13 关联到 } */
public final static int RBRACE = 13;
/** 14 关联到 [ */
public final static int LBRACKET = 14;
/** 15 关联到 ] */
public final static int RBRACKET = 15;
/** 16 关联到 , */
public final static int COMMA = 16;
/** 17 关联到 : */
public final static int COLON = 17;
/** 18 关联到 ident */
public final static int IDENTIFIER = 18;
/** 19 关联到 fieldName */
public final static int FIELD_NAME = 19;
/** 20 关联到 EOF */
public final static int EOF = 20;
/** 21 关联到 Set */
public final static int SET = 21;
/** 22 关联到 TreeSet */
public final static int TREE_SET = 22;
/** 23 关联到 undefined */
public final static int UNDEFINED = 23; // undefined
/** 24 关联到 ; */
public final static int SEMI = 24;
/** 25 关联到 . */
public final static int DOT = 25;
/** 26 关联到 hex */
public final static int HEX = 26;
public static String name(int value) {
switch (value) {
case ERROR:
return "error";
case LITERAL_INT:
return "int";
case LITERAL_FLOAT:
return "float";
case LITERAL_STRING:
return "string";
case LITERAL_ISO8601_DATE:
return "iso8601";
case TRUE:
return "true";
case FALSE:
return "false";
case NULL:
return "null";
case NEW:
return "new";
case LPAREN:
return "(";
case RPAREN:
return ")";
case LBRACE:
return "{";
case RBRACE:
return "}";
case LBRACKET:
return "[";
case RBRACKET:
return "]";
case COMMA:
return ",";
case COLON:
return ":";
case SEMI:
return ";";
case DOT:
return ".";
case IDENTIFIER:
return "ident";
case FIELD_NAME:
return "fieldName";
case EOF:
return "EOF";
case SET:
return "Set";
case TREE_SET:
return "TreeSet";
case UNDEFINED:
return "undefined";
case HEX:
return "hex";
default:
return "Unknown";
}
}
接下来,我们继续分析如何实现具体token解析的。
JSONLexerBase定义并实现了json串实现解析机制的基础,它对于反序列化很重要,我们先来理解它的含义:
/** 当前token含义 */
protected int token;
/** 记录当前扫描字符位置 */
protected int pos;
protected int features;
/** 当前有效字符 */
protected char ch;
/** 流(或者json字符串)中当前的位置,每次读取字符会递增 */
protected int bp;
protected int eofPos;
/** 字符缓冲区 */
protected char[] sbuf;
/** 字符缓冲区的索引,指向下一个可写
* 字符的位置,也代表字符缓冲区字符数量
*/
protected int sp;
/**
* number start position
* 可以理解为 找到token时 token的首字符位置
* 和bp不一样,这个不会递增,会在开始token前记录一次
*/
protected int np;
JSONLexerBase成员函数
在开始分析词法分析实现过程中,我发现中解析存在大量重复代码实现或极其类似实现,重复代码主要解决类似c++内联调用,极其相似代码实现我会挑选有代表性的来说明(一般实现较为复杂),没有说明的成员函数可以参考代码注释
推断token类型
fastjson token类型推断当前json字符串是那种类型的token, 比如是字符串、花括号和逗号等等。
public final void nextToken() {
/** 将字符buffer pos设置为初始0 */
sp = 0;
for (;;) {
/** pos记录为流的当前位置 */
pos = bp;
if (ch == '/') {
/** 如果是注释// 或者 \/* *\/ 注释,跳过注释 */
skipComment();
continue;
}
if (ch == '"') {
/** 读取引号内的字符串 */
scanString();
return;
}
if (ch == ',') {
/** 跳过当前,读取下一个字符 */
next();
token = COMMA;
return;
}
if (ch >= '0' && ch <= '9') {
/** 读取整数 */
scanNumber();
return;
}
if (ch == '-') {
/** 读取负数 */
scanNumber();
return;
}
switch (ch) {
/** 读取单引号后面的字符串,和scanString逻辑一致 */
case '\'':
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("Feature.AllowSingleQuotes is false");
}
scanStringSingleQuote();
return;
case ' ':
case '\t':
case '\b':
case '\f':
case '\n':
case '\r':
next();
break;
case 't': // true
/** 读取字符true */
scanTrue();
return;
case 'f': // false
/** 读取字符false */
scanFalse();
return;
case 'n': // new,null
/** 读取为new或者null的token */
scanNullOrNew();
return;
case 'T':
case 'N': // NULL
case 'S':
case 'u': // undefined
/** 读取标识符,已经自动预读了下一个字符 */
scanIdent();
return;
case '(':
/** 读取下一个字符 */
next();
token = LPAREN;
return;
case ')':
next();
token = RPAREN;
return;
case '[':
next();
token = LBRACKET;
return;
case ']':
next();
token = RBRACKET;
return;
case '{':
next();
token = LBRACE;
return;
case '}':
next();
token = RBRACE;
return;
case ':':
next();
token = COLON;
return;
case ';':
next();
token = SEMI;
return;
case '.':
next();
token = DOT;
return;
case '+':
next();
scanNumber();
return;
case 'x':
scanHex();
return;
default:
if (isEOF()) { // JLS
if (token == EOF) {
throw new JSONException("EOF error");
}
token = EOF;
pos = bp = eofPos;
} else {
/** 忽略控制字符或者删除字符 */
if (ch <= 31 || ch == 127) {
next();
break;
}
lexError("illegal.char", String.valueOf((int) ch));
next();
}
return;
}
}
}
跳过注释
解析注释主要分为两种,支持// 或者 /* */ 注释格式。
protected void skipComment() {
/** 读下一个字符 */
next();
/** 连续遇到左反斜杠/ */
if (ch == '/') {
for (;;) {
/** 读下一个字符 */
next();
if (ch == '\n') {
/** 如果遇到换行符,继续读取下一个字符并返回 */
next();
return;
/** 如果已经遇到流结束,返回 */
} else if (ch == EOI) {
return;
}
}
/** 遇到`/*` 注释的格式 */
} else if (ch == '*') {
/** 读下一个字符 */
next();
for (; ch != EOI;) {
if (ch == '*') {
/** 如果遇到*,继续尝试读取下一个字符,看看是否是/字符 */
next();
if (ch == '/') {
/** 如果确实是/字符,提前预读下一个有效字符后终止 */
next();
return;
} else {
/** 遇到非/ 继续跳过度下一个字符 */
continue;
}
}
/** 如果没有遇到`*\` 注释格式, 继续读下一个字符 */
next();
}
} else {
/** 不符合// 或者 \/* *\/ 注释格式 */
throw new JSONException("invalid comment");
}
}
扫描字符串
当解析json字符串是"时,会调用扫描字符串方法。
public final void scanString() {
/** 记录当前流中token的开始位置, np指向引号的索引 */
np = bp;
hasSpecial = false;
char ch;
for (;;) {
/** 读取当前字符串的字符 */
ch = next();
/** 如果遇到字符串结束符", 则结束 */
if (ch == '\"') {
break;
}
if (ch == EOI) {
/** 如果遇到了结束符EOI,但是没有遇到流的结尾,添加EOI结束符 */
if (!isEOF()) {
putChar((char) EOI);
continue;
}
throw new JSONException("unclosed string : " + ch);
}
/** 处理转译字符逻辑 */
if (ch == '\\') {
if (!hasSpecial) {
/** 第一次遇到\认为是特殊符号 */
hasSpecial = true;
/** 如果buffer空间不够,执行2倍扩容 */
if (sp >= sbuf.length) {
int newCapcity = sbuf.length * 2;
if (sp > newCapcity) {
newCapcity = sp;
}
char[] newsbuf = new char[newCapcity];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
/** 复制有效字符串到buffer中,不包括引号 */
copyTo(np + 1, sp, sbuf);
// text.getChars(np + 1, np + 1 + sp, sbuf, 0);
// System.arraycopy(buf, np + 1, sbuf, 0, sp);
}
/** 读取转译字符\下一个字符 */
ch = next();
/** 转换ascii字符,请参考:https://baike.baidu.com/item/ASCII/309296?fr=aladdin */
switch (ch) {
case '0':
/** 空字符 */
putChar('\0');
break;
case '1':
/** 标题开始 */
putChar('\1');
break;
case '2':
/** 正文开始 */
putChar('\2');
break;
case '3':
/** 正文结束 */
putChar('\3');
break;
case '4':
/** 传输结束 */
putChar('\4');
break;
case '5':
/** 请求 */
putChar('\5');
break;
case '6':
/** 收到通知 */
putChar('\6');
break;
case '7':
/** 响铃 */
putChar('\7');
break;
case 'b': // 8
/** 退格 */
putChar('\b');
break;
case 't': // 9
/** 水平制表符 */
putChar('\t');
break;
case 'n': // 10
/** 换行键 */
putChar('\n');
break;
case 'v': // 11
/** 垂直制表符 */
putChar('\u000B');
break;
case 'f': // 12
/** 换页键 */
case 'F':
/** 换页键 */
putChar('\f');
break;
case 'r': // 13
/** 回车键 */
putChar('\r');
break;
case '"': // 34
/** 双引号 */
putChar('"');
break;
case '\'': // 39
/** 闭单引号 */
putChar('\'');
break;
case '/': // 47
/** 斜杠 */
putChar('/');
break;
case '\\': // 92
/** 反斜杠 */
putChar('\\');
break;
case 'x':
/** 小写字母x, 标识一个字符 */
char x1 = ch = next();
char x2 = ch = next();
/** x1 左移4位 + x2 */
int x_val = digits[x1] * 16 + digits[x2];
char x_char = (char) x_val;
putChar(x_char);
break;
case 'u':
/** 小写字母u, 标识一个字符 */
char u1 = ch = next();
char u2 = ch = next();
char u3 = ch = next();
char u4 = ch = next();
int val = Integer.parseInt(new String(new char[] { u1, u2, u3, u4 }), 16);
putChar((char) val);
break;
default:
this.ch = ch;
throw new JSONException("unclosed string : " + ch);
}
continue;
}
/** 没有转译字符,递增buffer字符位置 */
if (!hasSpecial) {
sp++;
continue;
}
/** 继续读取转译字符后面的字符 */
if (sp == sbuf.length) {
putChar(ch);
} else {
sbuf[sp++] = ch;
}
}
token = JSONToken.LITERAL_STRING;
/** 自动预读下一个字符 */
this.ch = next();
}
在解析到字符串的时候会自动写入buffer
扫描数字类型
public final void scanNumber() {
/** 记录当前流中token的开始位置, np指向数字字符索引 */
np = bp;
/** 兼容处理负数 */
if (ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
/** 如果是数字字符,递增索引位置 */
sp++;
} else {
break;
}
next();
}
boolean isDouble = false;
/** 如果遇到小数点字符 */
if (ch == '.') {
sp++;
/** 继续读小数点后面字符 */
next();
isDouble = true;
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
}
/** 继续读取数字后面的类型 */
if (ch == 'L') {
sp++;
next();
} else if (ch == 'S') {
sp++;
next();
} else if (ch == 'B') {
sp++;
next();
} else if (ch == 'F') {
sp++;
next();
isDouble = true;
} else if (ch == 'D') {
sp++;
next();
isDouble = true;
} else if (ch == 'e' || ch == 'E') {
/** 扫描科学计数法 */
sp++;
next();
if (ch == '+' || ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
if (ch == 'D' || ch == 'F') {
sp++;
next();
}
isDouble = true;
}
if (isDouble) {
token = JSONToken.LITERAL_FLOAT;
} else {
token = JSONToken.LITERAL_INT;
}
}
扫描Boolean
public final void scanTrue() {
if (ch != 't') {
throw new JSONException("error parse true");
}
next();
if (ch != 'r') {
throw new JSONException("error parse true");
}
next();
if (ch != 'u') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b' || ch == ':' || ch == '/') {
/** 兼容性防御,标记是true的token */
token = JSONToken.TRUE;
} else {
throw new JSONException("scan true error");
}
}
扫描标识符
public final void scanIdent() {
/** 记录当前流中token的开始位置, np指向当前token前一个字符 */
np = bp - 1;
hasSpecial = false;
for (;;) {
sp++;
next();
/** 如果是字母或数字,继续读取 */
if (Character.isLetterOrDigit(ch)) {
continue;
}
/** 获取字符串值 */
String ident = stringVal();
if ("null".equalsIgnoreCase(ident)) {
token = JSONToken.NULL;
} else if ("new".equals(ident)) {
token = JSONToken.NEW;
} else if ("true".equals(ident)) {
token = JSONToken.TRUE;
} else if ("false".equals(ident)) {
token = JSONToken.FALSE;
} else if ("undefined".equals(ident)) {
token = JSONToken.UNDEFINED;
} else if ("Set".equals(ident)) {
token = JSONToken.SET;
} else if ("TreeSet".equals(ident)) {
token = JSONToken.TREE_SET;
} else {
token = JSONToken.IDENTIFIER;
}
return;
}
}
扫描十六进制数
public final void scanHex() {
if (ch != 'x') {
throw new JSONException("illegal state. " + ch);
}
next();
/** 十六进制x紧跟着单引号 */
/** @see com.alibaba.fastjson.serializer.SerializeWriter#writeHex(byte[]) */
if (ch != '\'') {
throw new JSONException("illegal state. " + ch);
}
np = bp;
/** 这里一次next, for循环也读一次next, 因为十六进制被写成2个字节的单字符 */
next();
for (int i = 0;;++i) {
char ch = next();
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
sp++;
continue;
} else if (ch == '\'') {
sp++;
/** 遇到结束符号,自动预读下一个字符 */
next();
break;
} else {
throw new JSONException("illegal state. " + ch);
}
}
token = JSONToken.HEX;
}