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;
this.input = input;
this.config = config;
this.symbolTable = config.symbolTable;
int ch = lexer.getCurrent();
if (ch == '{') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACE;
} else if (ch == '[') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
} else {
lexer.nextToken();
}
}
方法内详细记录了一个通用反序列化器所需要的数据等内容,并且在初始化(调用构造函数)时就将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) {
lexer.nextToken(JSONToken.COMMA);
return null;
}
if (lexer.token() != JSONToken.LBRACKET) {
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) {
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) {
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;
if (!((clazz == byte[].class || clazz == char[].class) && lexer.token() == LITERAL_STRING)) {
isArray = clazz.isArray();
componentType = clazz.getComponentType();
}
}
}
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) {
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);
开始。 感谢各位老师的阅读与指导!
|