IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> fastjson源码解析——反序列化(八) -> 正文阅读

[开发测试]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[]调用传入调用parserparseArray()方法,详见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的内部逻辑有一定区别。

  1. 由于本方法针对不同类型的对象,传入了Type[]的数据,因此需要对每个位置的对象都进行类型检查,检查一次常见数据类型,若不是常见类型,就创建专属的反序列化实例,使用这个实例对JSON对象进行反序列化,将结果保存在Object[]
  2. 单一类型的反序列化过程则显得有些简单粗暴,由于不需要考虑类型问题,单一类型的反序列化实例只有一个,使用同一个实例完成了所有对象的反序列化,将内容存放在List

最后

本文对不同类型的JSON对象数组反序列化内部逻辑展开分析,通过与单一类型数组反序列化方法的对比,明晰了二者的异同点,从相同点中探寻数组反序列化的大致思路,从不同点中发现规定类型对象数组处理的特殊之处。
下一次,我们将继续细化对fastjson对象数组反序列化的解析,从parseArray(String, Type[], ParserConfig)方法的

        parser.handleResovleTask(list);

开始。
感谢各位老师的阅读与指导!

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-12-14 16:15:41  更:2021-12-14 16:17:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 7:31:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码