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);
开始
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
的处理、存储
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[]
,使用上文构造的通用反序列化器执行数组反序列化操作。
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);
开始。
感谢各位老师的阅读与指导!