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

2021SC@SDUSC

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

概要

根据之前的分析,fastjson可以简单分类成两类,序列化部分toJSONString和反序列化部分parseObject。反序列化的含义,指将JSON字符串(类型String)转化为Java对象。
我负责的部分是parseArray部分。简单来说,可以理解为把JSON字符串内包含的多个JSON对象转化为Java的List类型对象。因为parseArray的底层原理和parseObject相同,所以我为了更好地开始parseArray部分的研究,先开始了对parse部分源码解析。
本文对fastjson中parseObject方法的代码展开解析

现在,我们正式开始反序列化源码的探究

parse的API调用方式

在以前的学习过程中,我深深感到使用各语言的debug工具,合理设置断点,步步跟随程序运行、适时查看内存变量数据可以有效帮助程序员学习不了解的代码。

我写了一个简单的程序,来调用fastjson的功能

下面是API调用程序

package test;
import com.alibaba.fastjson.JSON;

public class test {
    public static void main(String[] args) {
        People people1=new People();
        People.setName("Sam");
        People.setId(1);
        People.setDescription("good");//调用JavaBean的Getter、Setter生成people1对象
        String str= JSON.toJSONString(people);//调用序列化方法,生成JSONString
        People people2=JSON.parseObject(str, People.class);//我的探究重点方法
    }
}

其中,People类必须是一个符合JavaBean的类
People.class:

package test
public class People{
    private String name;
    private int id;
    private String description;
    public people(){};
    Getter&Setter//JavaBean,可以使用IDE自动生成,不贅叙
}

使用IDE的debug工具,在JSON.parseObject方法设置断点,查看内存中people2的变量情况,发现people2内各属性值均与people1相同,说明fastjson成功将people1序列化的JSONString反序列化。

正式开始

在本节,我将一步步从parseObejct深入到最底层的方法,研究相互之间的关系

1. parseObject()

这是fastjson反序列化的入口API方法,提供给用户直接调用。他的方法参数只有String的JSONString和用户希望转化成的clazz类。返回一个clazz类的对象。

fastjson的API一直很简洁,对于普通用户来说,只需要知道序列化和反序列化的两个接口即可,内部的所有操作均由fastjson自己完成

public static <T> T parseObject(String text, Class<T> clazz) {
        /** 传入JSONString,根据clazz转化,返回一个clazz类的对象 */
        return parseObject(text, clazz, new Feature[0]);
    }

特别说明,这里的clazz不是写错,而是程序员为了表示“类”的含义,又不能使用保留字class,约定使用clazz代替

这个方法是以下所有方法的最终调用者,也是直接提供服务的方法。

这个反序列化接口可以处理对象包含的任意字段类型

2. 还是parseObject()

这个方法也是fastjson提供的API之一,也是上一个parseObject方法调用的函数。这个方法不仅包含了JSONString和类,还多了features数组,存放所有的反序列化特性(可以理解为configure

public static <T> T parseObject(String json, Class<T> clazz, Feature... features) {
        return (T) parseObject(json, (Type) clazz, ParserConfig.global, null, DEFAULT_PARSER_FEATURE, features);
    }

同样,他也调用了另外的parseObject方法,虽然这样看起来跳转有点多,但是细想,这样的操作在程序中形成了明显的相互调用关系。
若某处出现错误,只需要修改极小部分的代码即可修复,一处错误只需要修改一个函数,不会发生大范围修改。


此方法是上文中被调用的方法,也是parseObject系列函数出现的第一个写了实际内容的函数。前面的方法看似累赘,但它们的存在简化了API接口,极大减轻了初学者和轻度使用者的学习成本,毕竟一个简介的API是所有程序员的最爱。下面详细介绍代码。
这个方法规定了输入:
String input
Type clazz
*.parser.ParserConfig config
*.parser.deserializer.ParseProcess processor
int featureValues
Feature[] features

public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,int featureValues, Feature... features) {
        if (input == null || input.length() == 0) {
            return null;//避免对空字符串操作
        }
        /** 配置反序列化的特性features */
        if (features != null) {
            for (Feature feature : features) {
                featureValues |= feature.mask;
            }
        }
        /** new一个JSONparser,反序列化的数据类型由其查找所有具体类型的反序列化器 */
        DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
        if (processor != null) {
            if (processor instanceof ExtraTypeProvider) {
                parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
            }
            if (processor instanceof ExtraProcessor) {
                parser.getExtraProcessors().add((ExtraProcessor) processor);
            }
            if (processor instanceof FieldTypeResolver) {
                parser.setFieldTypeResolver((FieldTypeResolver) processor);
            }
        }
        /** 使用反序列化实例parser转换对象 */
        T value = (T) parser.parseObject(clazz, null);
        parser.handleResovleTask(value);
        parser.close();
        return (T) value;
    }

这个方法表示了反序列化的核心架构,是今后一个研究大重点。
反序列化函数parseObejct定义了反序列化的大框架:

  1. 创建配置config,其中包括了所有的反序列化特性配置
  2. 添加反序列化的processor
  3. 根据具体类型,查找反序列化实例,执行反序列化

整个反序列化的核心,在于第三步,如何查找到具体的反序列化实例,前两步都是为了设置各种特性化配置
我们继续看这一步(重点)

T value = (T) parser.parseObject(clazz, null);

3. <T> T parseObject(Type type, Object fieldName)

从这个方法开始,我们的反序列化之旅开始从大框架走向具体化,例如具体数据类型的反序列化实例匹配、继承了常见类的类的反序列化实例匹配等等。自此,我们正式开始了针对不同(具体)类型的反序列化进程。

此方法也是parseObject系列的最后一个方法,因为从此开始需要对每个不同类型的数据进行反序列化操作。

public <T> T parseObject(Type type, Object fieldName) {
        int token = lexer.token();
        /** 获取JSON字符串的第一个token */
        if (token == JSONToken.NULL) {
            lexer.nextToken();
            return (T) TypeUtils.optionalEmpty(type);
        }
        /** 根据token确定JSON字符串某位置的类型(包含JSON对象开始结束等特殊位置的字符 */
        if (token == JSONToken.LITERAL_STRING) {
            /** 若类型为字节数据 */
            if (type == byte[].class) {
                byte[] bytes = lexer.bytesValue();
                lexer.nextToken();
                return (T) bytes;
            }
            /** 获取到char类型数据 */
            if (type == char[].class) {
                String strVal = lexer.stringVal();
                lexer.nextToken();
                return (T) strVal.toCharArray();
            }
        }
        /** 使用config对特定类型的查找反序列化实例,核心方法之一 */
        ObjectDeserializer deserializer = config.getDeserializer(type);
        try {
            if (deserializer.getClass() == JavaBeanDeserializer.class) {
                /** 检查JSONString的语法正确性 */
                if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
                throw new JSONException("syntax error,expect start with { or [,but actually start with "+ lexer.tokenName());
            }
                /** 属于JavaBeanDeserializer类型(fastjson的内部类型),简单处理 */
                return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
            } else {
                /** 核心方法之二,执行反序列化 */
                return (T) deserializer.deserialze(this, type, fieldName);
            }
        } catch (JSONException e) {
            throw e;
        } catch (Throwable e) {
            throw new JSONException(e.getMessage(), e);
        }
    }

从这个方法的代码分析,很容易可以得出这样的结论:
反序列化关键在于根据不同的类型,查找到对应的反序列化实例;而这种类型匹配的思路是很巧妙的,在后续的介绍中会详细介绍fastjson团队的巧思。

最后

fastjson反序列化的部分暂时介绍到这里。本文从fastjson的API调用开始,使用IntelliJ IDEA的debug工具,一步步深入fastjson代码,介绍到最后一个parseObject方法。

  • 从使用者的角度看,这些方法构成了一个对初学者友好的API接口,也为需要深度定制json转化服务的用户提供了可能
  • 从代码角度来看,这些方法构成了一个易于维护、层级严密的调用层级关系。便于开发者调试,也便于开源代码学习者理解fastjson的代码逻辑
    下次我计划从这里开始,分析

    ObjectDeserializer deserializer = config.getDeserializer(type);

    这个方法如何处理不同的类
    感谢各位老师的阅读指导!

2人评论了“fastjson源码解析——反序列化(一)”

发表评论