fastjson源码分析-JSONToken
2021/12/26 17:10:25
本文主要是介绍fastjson源码分析-JSONToken,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
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; }
这篇关于fastjson源码分析-JSONToken的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16Vue3资料:新手入门必读教程
- 2024-11-16Vue3资料:新手入门全面指南
- 2024-11-16Vue资料:新手入门完全指南
- 2024-11-16Vue项目实战:新手入门指南
- 2024-11-16React Hooks之useEffect案例详解
- 2024-11-16useRef案例详解:React中的useRef使用教程
- 2024-11-16React Hooks之useState案例详解
- 2024-11-16Vue入门指南:从零开始搭建第一个Vue项目
- 2024-11-16Vue3学习:新手入门教程与实践指南
- 2024-11-16Vue3学习:从入门到初级实战教程