fastjson源码解析——反序列化(八)
作者:互联网
2021SC@SDUSC
本文在个人博客同步发出,地址Redbit的个人历程
文章目录
概要
上一篇fastjson源码解析——反序列化(七)中,我们深入探究了parseArray(String, Class<T>)
这个针对单一类型JSON对象数组的反序列化API,从最外层的parseArray(Class<?>, Collection)
开始,看到其内部对常见类型(int
, String
)的快速处理、对普通类型对象反序列化实例的注册、使用等。本文将从parseArray(String, Type[])
注重对每一个JSON对象规定对象类型的API,探究内部实现的逻辑,并着重于与单一类型反序列化API逻辑实现对比,通过对比看出fastjson开发者的巧思
前文提到,对于规定了每个元素类型的JSON数组反序列化,先使用JSON字符串和用户配置config
创建通用的反序列化器,再将每一个元素规定的类型数组Type[]
调用传入调用parser
的parseArray()
方法,详见fastjson源码解析——反序列化(六)
本文继续第6篇的步伐,从
DefaultJSONParser parser = new DefaultJSONParser(text, config);
开始
1. DefaultJSONParser(final String, final ParserConfig)
构造函数
这一步fastjson将JSON字符串传入通用反序列化器的构造函数,同时照顾到用户自定的相关配置信息。
代码:
public DefaultJSONParser(final String input, final ParserConfig config){
this(input, new JSONScanner(input, JSON.DEFAULT_PARSER_FEATURE), config);
}
方法代码很简单,是一个结构性的构造函数,可以看到它调用了另外一个构造函数,传入了JSONScanner
对象。
实际上这个JSONScanner
就是我们在单一类型对象数组反序列化的parseArray(Type, Collection, Object)
方法中整合的token
内容的专用扫描器,传入输入的JSON字符串,生成对这个字符串的扫描结果。
单一类型对象反序列化过程中,由于其对反序列化token
的使用较为简单,可以直接整合于过程内,因此并没有单独分列出一些专门处理JSON字符串token
的方法。
在规定了每个元素类型的API里,对于不同类型对象的反序列化操作复杂得多,对token
的检索也不简单,整合在一个方法内只会导致代码方法太长,不易维护。
下面进入这个构造函数,再看看内部对token
的处理、存储
2. DefaultJSONParser(final Object, final JSONLexer, final ParserConfig)
构造函数
这一步包含了对token
的处理、存储的操作。可以参考代码内我加的注释:
public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config){
this.lexer = lexer; // 存储token信息
this.input = input; // 存储JSON字符串
this.config = config; // 存储用户配置(可能为系统默认配置GlobalConfig)
this.symbolTable = config.symbolTable;
int ch = lexer.getCurrent(); //获取当前位置的token
if (ch == '{') { // {是一个对象的开始标志
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACE;
// 在token中标记对象开始的位置
} else if (ch == '[') { // [是一个数组的开始标志
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
// 在token中标记数组起始位置,同上
} else {
lexer.nextToken(); // 跳转到下一个token
}
}
方法内详细记录了一个通用反序列化器所需要的数据等内容,并且在初始化(调用构造函数)时就将token
的指针指向对象或对象数组的开始,方便后续实际操作。
这样,所有需要的数据备齐,一个通用的反序列化器构造就完成了,返回到主线parseArray(String, Type[], ParserConfig)
方法。
下一步即为
Object[] objectArray = parser.parseArray(types);
传入每个元素对应位置的类型数组Type[]
,使用上文构造的通用反序列化器执行数组反序列化操作。
3. parseArray(Type[])
方法
本方法涉及到对token
的使用,以及具体类型对象的反序列化操作。
长代码警告,可以参考代码里我自己写的注释,快速理解
public Object[] parseArray(Type[] types) {
if (lexer.token() == JSONToken.NULL) {
// 空token,跳转下一个逗号,返回空数组,避免后续操作调用到空的token内容
lexer.nextToken(JSONToken.COMMA);
return null;
}
if (lexer.token() != JSONToken.LBRACKET) { // 检查JSON字符串语法错误
throw new JSONException("syntax error : " + lexer.tokenName());
}
Object[] list = new Object[types.length]; // 创建存放反序列化结果对象的数组
if (types.length == 0) {
lexer.nextToken(JSONToken.RBRACKET);
if (lexer.token() != JSONToken.RBRACKET) { // 仍然检查语法错误,缺少}结束对象
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.COMMA); // 若检查无误,跳转下一个逗号,返回结果
return new Object[0];
}
lexer.nextToken(JSONToken.LITERAL_INT);
for (int i = 0; i < types.length; ++i) {
Object value;
if (lexer.token() == JSONToken.NULL) {
value = null;
lexer.nextToken(JSONToken.COMMA);
} else {
Type type = types[i];
if (type == int.class || type == Integer.class) {
// int类型,包括包装器Integer
if (lexer.token() == JSONToken.LITERAL_INT) {
value = Integer.valueOf(lexer.intValue());
lexer.nextToken(JSONToken.COMMA);
} else {
value = this.parse();
value = TypeUtils.cast(value, type, config);
}
} else if (type == String.class) { // String类型,同上
if (lexer.token() == JSONToken.LITERAL_STRING) {
value = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);
} else {
value = this.parse();
value = TypeUtils.cast(value, type, config);
}
} else {
boolean isArray = false;
Class<?> componentType = null;
if (i == types.length - 1) {
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
//如果最后一个type是字节数组,且当前token为字符串类型,不应该当作可变长参数进行处理
//而是作为一个整体的Base64字符串进行反序列化
if (!((clazz == byte[].class || clazz == char[].class) && lexer.token() == LITERAL_STRING)) {
isArray = clazz.isArray();
componentType = clazz.getComponentType();
}
}
}
// 对varArgs提供支持
if (isArray && lexer.token() != JSONToken.LBRACKET) {
List<Object> varList = new ArrayList<Object>();
ObjectDeserializer deserializer = config.getDeserializer(componentType);
int fastMatch = deserializer.getFastMatchToken();
if (lexer.token() != JSONToken.RBRACKET) {
for (;;) {
Object item = deserializer.deserialze(this, type, null);
varList.add(item);
if (lexer.token() == JSONToken.COMMA) {
lexer.nextToken(fastMatch);
} else if (lexer.token() == JSONToken.RBRACKET) {
break;
} else {
throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
}
}
}
value = TypeUtils.cast(varList, type, config);
} else {
ObjectDeserializer deserializer = config.getDeserializer(type);
value = deserializer.deserialze(this, type, i);
}
}
}
list[i] = value;
if (lexer.token() == JSONToken.RBRACKET) { // 读取到},对象结束,跳出循环
break;
}
if (lexer.token() != JSONToken.COMMA) { // JSON语法错误
throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
}
if (i == types.length - 1) {
lexer.nextToken(JSONToken.RBRACKET);
} else {
lexer.nextToken(JSONToken.LITERAL_INT);
}
}
if (lexer.token() != JSONToken.RBRACKET) {
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.COMMA);
return list;
}
此方法看起来很复杂,但实际逻辑与单一类型数组反序列化的操作差别不大,都是先检查常见类型,将此类型的反序列化实例保存,等待反序列化;若不是常见类型,就新产生一个反序列化实例。
而后,对JSON数组,使用token将元素分离开;对每个元素调用反序列化实例。
需要注意的是,这里与单一类型API的内部逻辑有一定区别。
- 由于本方法针对不同类型的对象,传入了
Type[]
的数据,因此需要对每个位置的对象都进行类型检查,检查一次常见数据类型,若不是常见类型,就创建专属的反序列化实例,使用这个实例对JSON对象进行反序列化,将结果保存在Object[]
中 - 单一类型的反序列化过程则显得有些简单粗暴,由于不需要考虑类型问题,单一类型的反序列化实例只有一个,使用同一个实例完成了所有对象的反序列化,将内容存放在
List
内
最后
本文对不同类型的JSON对象数组反序列化内部逻辑展开分析,通过与单一类型数组反序列化方法的对比,明晰了二者的异同点,从相同点中探寻数组反序列化的大致思路,从不同点中发现规定类型对象数组处理的特殊之处。
下一次,我们将继续细化对fastjson对象数组反序列化的解析,从parseArray(String, Type[], ParserConfig)
方法的
parser.handleResovleTask(list);
开始。
感谢各位老师的阅读与指导!
标签:fastjson,JSONToken,lexer,token,源码,类型,序列化,final 来源: https://blog.csdn.net/lllhhhyyy999/article/details/121886177