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

2021/12/12 17:21:37

本文主要是介绍fastjson源码解析——反序列化(八),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

2021SC@SDUSC

本文在个人博客同步发出,地址Redbit的个人历程

文章目录

    • 概要
    • 1. `DefaultJSONParser(final String, final ParserConfig)`构造函数
    • 2. `DefaultJSONParser(final Object, final JSONLexer, final ParserConfig)`构造函数
    • 3. `parseArray(Type[])`方法
    • 最后

概要

上一篇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);

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



这篇关于fastjson源码解析——反序列化(八)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程