fastjson源码解析——反序列化(五)

2021SC@SDUSC

本文在CSDN个人博客同步发出,地址CSDN博客

概要

上一篇fastjson源码解析——反序列化(四)介绍了fastjson中parseArray()不同API各自的使用方式。本文,我们将一步步,开始从parseArray(String,Class<T>)逐渐深入json对象数组反序列化的过程。
上次提到,fastjson的API设计非常简洁。对于一般的使用者,对象反序列化只需要使用parseObject()系列API,数组反序列化也可以只使用parseArray()API即可实现。fastjson的设计对初次使用者表现了极大的友好。
我们使用与最初分析对象反序列化相似的步骤,即编写demo、设置断点,步步深入,分析其中的重要方法
本文正式开始fastjson数组反序列化的研究

1. parseArray(String,Class<T>)方法

作为被我们调用的第一个,也是使用最广泛的API,parseArray(String,Class<T>)是fastjson对外提供服务的关键方法,也是上一篇,在demo中被调用的方法。
这个API是大多数fastjson使用者对json对象数组反序列化的入口,也是fastjson内部逻辑的开始。先看代码

public static <T> List<T> parseArray(String text, Class<T> clazz) {
    return parseArray(text, clazz, ParserConfig.global);
}

这部分的代码很简单,但是也蕴含着fastjson内部对反序列化逻辑的处理:有部分开发者对json对象数组的反序列化有特殊需求,需要定制反序列化的过程,即new出一个fastjson的配置对象,其中包含着所有开发者希望的特殊要求;但是对于大多数人来说,不需要特殊的config设置,只需要使用一个被普遍接受的反序列化方法。fastjson开发者考虑到了大部分人的学习成本、使用成本等,同时不放弃反序列化需求的定制方法,因此对需要配置的parse(),parseObject(),parseArray()这类方法都进一步包装,并设置了一个通用、全局的配置(static ParserConfig.global)让程序调用。

这样的操作,使得程序的代码在不冗余的情况下,实现了面向不同使用场景、人群的多种API。
日常设计程序时,我们总是会看到代码的冗余,但是囿于代码逻辑已经固化,无法有效去除冗余部分的代码(这个情况在前端开发尤其明显)。
虽然说我们无法做到同阿里巴巴开发者相同的代码整洁、可读性,但是如果事先考虑好代码结构,写好文档,我们也应该要尽可能降低代码的冗余,毕竟冗余情况直接影响到程序的性能降低

下面,我们深入下一个方法parseArray(text, clazz, ParserConfig.global),这个方法涉及到配置的转发、数据的传递等等

2. parseArray(String, Class<T>, ParserConfig)方法

这个方法是fastjson对象数组反序列化API层面较为重要的方法之一。它使用配置创建了通用的反序列化器,按照逻辑使用反序列化器对字符串展开操作。
先看代码:

    public static <T> List<T> parseArray(String text, Class<T> clazz, ParserConfig config) {
        if (text == null) {
            return null; // 快速返回,避免错误数据深入,从根源上减少后续代码出错的可能性(NullPointerException,etc)
        }

        List<T> list; // 创建存放反序列化器输出的列表

        DefaultJSONParser parser = new DefaultJSONParser(text, config);
        // 根据配置对象和JSON字符串数据,创建默认JSON反序列化器
        JSONLexer lexer = parser.lexer;
        int token = lexer.token();
        /** 预先判断json字符串的合法性 */
        if (token == JSONToken.NULL) { // 根据token大致识别json字符串的合法性(token定义见前文:反序列化特辑)
            lexer.nextToken();
            list = null;
        } else if (token == JSONToken.EOF && lexer.isBlankInput()) {
            list = null;
        } else {
            list = new ArrayList<T>();
            parser.parseArray(clazz, list); // 反序列化

            parser.handleResovleTask(list);
        }

        parser.close();

        return list;
    }

本方法规定了对于存储了单一类型的JSON对象数组的字符串,在进行反序列化操作时所需要的所有信息。需要特别注意的是,类间关系extends,implements等等也可视为单一类型,但传入Class<T>时需要传入所有实例的公共超类。若需要对JSON对象数组的每一个元素进行特异性的匹配,需要调用另外一个APIList<Object> parseArray(String, Type[]),规定每个元素对应的类型即可。
可以看出,对象数组反序列化的操作大致步骤为:

  1. 根据传入的textconfig创建通用的反序列化器parser

  2. 保存token,使用token预检查传入字符串的合法性

    这时候细心的读者可能会发现,这部分和parseObject()API调用有些许区别。
    parseObject()调用时,不会显式地出现token,也不会在调用通用反序列化器的同一层级使用token的内容。这个特性正好体现出JSON对象和对象数组的区别。
    JSON对象以{开始,}结束,而JSON对象数组以[开始,中间包括了几个JSON对象,对象间使用,(英文逗号comma)分开,最后以]结束。
    为了能够调用到parseObject()的功能相同的部分方法,parseArray()需要先行使用token对整个字符串的合法性(是否符合JSON对象数组的标准、是否真的存放JSON对象等)进行检查,最后使用转化器,将被token检索出的每个JSON对象的子字符串提交给parseObject()的API,复用代码。

  3. 创建存放反序列化结果对象的数组ArrayList<T>

  4. 执行反序列化操作

  5. 结果返回

最后

本文正式开始对fastjson对象数组反序列化的分析,从上文编写的demo调用的API开始,先对单一类型的对象数组反序列化APIparseArray(String, Class<T>)展开分析,并了解了fastjson对数组反序列化操作的逻辑框架。
下次,我们将从另外一个API:parseArray(String, Type[])开始分析,了解规定数组每个元素类型后的反序列化API调用过程与单一类型API调用的异同点。
感谢各位老师的阅读与指导!

发表评论