先码后看 FastJSON源码浅析 侵立删

转自:https://blog.****.net/Androidlushangderen/article/details/41808459


最近偶然间看到了以前写过的代码,发现了以前用的非常频繁的json解析框架fastjson。这个框架的确非常方便,帮助开发者省了很多的人工代码。以前那是因为没有时间写学习其中的原理,周末抽了点时间分析了下他的源码,可以作为不错的json解析框架的资料。主要分为2个步骤,1个是对象转json字符串,还有1个是json字符转对象的处理。

fastjson介绍

fastjson是阿里巴巴内部的json解析框架,目前代码已经开源在alibaba的github上了,等会想要研究的fastjson的同学可以上github进行源码下载。链接为https://github.com/alibaba/fastjson.git,上面一堆的开源项目,一找就找到了。fastjson是用java语言写的,可以放在eclipse上进行源码的阅读。

搭建fastjson源码阅读环境

这个必须得说一下,这个可不是根据源码文件,逐个文件在代码编辑器中打开再阅读,这样效率太低,下面介绍一下步骤:

1.到github上下载源码,这个很简单,谁都会。

2.将里面的com核心的代码目录解压出来,因为你下载的压缩包很多东西不是你所需要的。

先码后看 FastJSON源码浅析 侵立删

3.将下载好的fastjson代码在eclipse上进行代码关联,如果在没有任何操作之前,阅读代码会出现下面的状况:

先码后看 FastJSON源码浅析 侵立删

关联步骤为:

先码后看 FastJSON源码浅析 侵立删

4.执行完上述操作后,就可以查看源码像查找自己本地的代码一样了。

测试的环境

在运行fastjson的2个功能前,我写了个测试场景类和对象类,首先声明类A:

[java] view plain copy
  1. public class A {  
  2.     private int age;  
  3.     private String name;  
  4.       
  5.     public int getAge() {  
  6.         return age;  
  7.     }  
  8.     public void setAge(int age) {  
  9.         this.age = age;  
  10.     }  
  11.     public String getName() {  
  12.         return name;  
  13.     }  
  14.     public void setName(String name) {  
  15.         this.name = name;  
  16.     }  
  17.       
  18.     public void showInfo(){  
  19.         System.out.println(this.name + ":" + this.age);  
  20.     }  
  21. }  

然后是场景测试类的代码:

[java] view plain copy
  1. /** 
  2.  * fastjson测试场景类 
  3.  * @author lyq 
  4.  * 
  5.  */  
  6. public class Client {  
  7.     public static void main(String[] args){  
  8.         String str;  
  9.         A a = null;  
  10.         A a2 = new A();  
  11.           
  12.     //  B b = null;  
  13.     //  B b2 = new B();  
  14.           
  15.         a2.setAge(11);  
  16.         a2.setName("zs");  
  17.         str = JSON.toJSONString(a2);  
  18.           
  19.         System.out.println(str);  
  20.         a = JSON.parseObject(str, A.class);  
  21.           
  22.         a.showInfo();  
  23.     }  
  24. }  
测试程序不需要这么复杂,简简单单就好。下面是运行结果:

[java] view plain copy
  1. {"age":11,"name":"zs"}  
  2. zs:11  
正如想象中一样,非常正确。

对象转json字符

下面看看第一个过程的分析,就是对象转字符的过程。我们可以开始看代码:

[java] view plain copy
  1. a2.setAge(11);  
  2. a2.setName("zs");  
  3. str = JSON.toJSONString(a2);  
  4.   
  5. System.out.println(str);  
  6. a = JSON.parseObject(str, A.class);  
到了第3行往里点:

[java] view plain copy
  1. public static final String toJSONString(Object object) {  
  2.     return toJSONString(object, new SerializerFeature[0]);  
  3. }  
  4.   
  5. public static final String toJSONString(Object object, SerializerFeature... features) {  
  6.     SerializeWriter out = new SerializeWriter();  
  7.   
  8.     try {  
  9.         JSONSerializer serializer = new JSONSerializer(out);  
  10.         for (com.alibaba.fastjson.serializer.SerializerFeature feature : features) {  
  11.             serializer.config(feature, true);  
  12.         }  
  13.   
  14.         serializer.write(object);  
  15.   
  16.         return out.toString();  
  17.     } finally {  
  18.         out.close();  
  19.     }  
  20. }  
就会连续经过这2个方法,这里就蹦出了几个新的类定义,反正最后就是通过返回out.toString()的形式就是最终的json字符串了。out在这里是一个写入器,这个写入器在JSONSerializer包构造,然后调用write写入Object对象。下面是关键的write方法:

[java] view plain copy
  1. public final void write(Object object) {  
  2.     if (object == null) {  
  3.         out.writeNull();  
  4.         return;  
  5.     }  
  6.   
  7.     Class<?> clazz = object.getClass();  
  8.     ObjectSerializer writer = getObjectWriter(clazz);  
  9.   
  10.     try {  
  11.         writer.write(this, object, nullnull0);  
  12.     } catch (IOException e) {  
  13.         throw new JSONException(e.getMessage(), e);  
  14.     }  
  15. }  
获取ObjectSerializer写入类型,往里面写东西,这里的ObjectSerializer只是个接口:

[java] view plain copy
  1. public interface ObjectSerializer {  
  2.   
  3.     void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException;  
  4. }  
真正的操作在他的子类上,他的子类有如下:

先码后看 FastJSON源码浅析 侵立删

基本涵盖了所有的基本数据类型,我们抽出一个子类看看他是怎么写入的:

[java] view plain copy
  1. public class BooleanArraySerializer implements ObjectSerializer {  
  2.   
  3.     public static BooleanArraySerializer instance = new BooleanArraySerializer();  
  4.   
  5.     public final void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {  
  6.         SerializeWriter out = serializer.getWriter();  
  7.           
  8.         if (object == null) {  
  9.             if (out.isEnabled(SerializerFeature.WriteNullListAsEmpty)) {  
  10.                 out.write("[]");  
  11.             } else {  
  12.                 out.writeNull();  
  13.             }  
  14.             return;  
  15.         }  
  16.           
  17.         boolean[] array = (boolean[]) object;  
  18.         out.write('[');  
  19.         for (int i = 0; i < array.length; ++i) {  
  20.             if (i != 0) {  
  21.                 out.write(',');  
  22.             }  
  23.             out.write(array[i]);  
  24.         }  
  25.         out.write(']');  
  26.     }  
  27. }  
首先获取写入的目标对象,就是刚刚的out,写入流程很好理解,因为是数组,先下[,中间写具体的对象数据,在补上]补齐。其他的数据类型写入方式原理也大体一致。于是一个大体的类关系图就出来了:

先码后看 FastJSON源码浅析 侵立删

这里作者比较好的设计是针对不同的类型定义不同的数据类型写入器,但是调用的时候用了统一的调用形式。

json字符串转化为对象

这个过程从字面理解也知道一定比上面的操作难。这里面也涉及了很多层的调用,再次回到刚刚的场景类代码:

[java] view plain copy
  1. a2.setAge(11);  
  2. a2.setName("zs");  
  3. str = JSON.toJSONString(a2);  
  4.   
  5. System.out.println(str);  
  6. a = JSON.parseObject(str, A.class);  
到了最后一步代码的时候,停下,由于过程复杂,这里我设置了断点的调试方法,下面是我截的图片:

先码后看 FastJSON源码浅析 侵立删


先码后看 FastJSON源码浅析 侵立删


先码后看 FastJSON源码浅析 侵立删


先码后看 FastJSON源码浅析 侵立删


先码后看 FastJSON源码浅析 侵立删


关键来到了看一下这个方法,名为

[java] view plain copy
  1. @SuppressWarnings({ "unchecked""rawtypes" })  
  2.     public final Object parseObject(final Map object, Object fieldName) {  
  3.         final JSONLexer lexer = this.lexer;  
里面声明了一个lexer变量,他也是一个接口,下面有2个实现的子类:

先码后看 FastJSON源码浅析 侵立删

这个类就是一个扫描器性质的类,里面维护了一个text的json字符串对象,然后这个类就在里面逐个扫描,取出对应的键值对,放入对象中。

[java] view plain copy
  1. public final class JSONScanner extends JSONLexerBase {  
  2.   
  3.     private final String text;  
  4.   
  5.     public JSONScanner(String input){  
  6.         this(input, JSON.DEFAULT_PARSER_FEATURE);  
  7.     }  
  8.   
  9.     public JSONScanner(String input, int features){  
  10.         this.features = features;  
  11.   
  12.         text = input;  
  13.         bp = -1;  
  14.   
  15.         next();  
  16.         if (ch == 65279) {  
  17.             next();  
  18.         }  
  19.     }  
Input输入值作为text内部对象进行扫描,bp为扫描的下标。解析操作的核心就是通过这个扫描类实现的,主要步骤为3步

1.取出key

2.得到class类型

3.取出value

4.Object设置key,value。

第一步:

[java] view plain copy
  1. boolean isObjectKey = false;  
  2. Object key;  
  3. if (ch == '"') {  
  4.     key = lexer.scanSymbol(symbolTable, '"');  
  5.     lexer.skipWhitespace();  
  6.     ch = lexer.getCurrent();  
  7.     if (ch != ':') {  
  8.         throw new JSONException("expect ':' at " + lexer.pos() + ", name " + key);  
  9.     }  
  10. else if (ch == '}') {  
  11.     lexer.next();  
  12.     lexer.resetStringPosition();  
  13.     lexer.nextToken();  
  14.     return object;  
  15. else if (ch == '\'') {  
  16.     if (!isEnabled(Feature.AllowSingleQuotes)) {  
  17.         throw new JSONException("syntax error");  
  18.     }  
  19.   
  20.     key = lexer.scanSymbol(symbolTable, '\'');  
  21.     lexer.skipWhitespace();  
  22.     ch = lexer.getCurrent();  
第二步获取相关类实例,这里必然用了反射的思想;

[java] view plain copy
  1. if (key == JSON.DEFAULT_TYPE_KEY) {  
  2.     String typeName = lexer.scanSymbol(symbolTable, '"');  
  3.     Class<?> clazz = TypeUtils.loadClass(typeName);  
  4.   
  5.     if (clazz == null) {  
  6.         object.put(JSON.DEFAULT_TYPE_KEY, typeName);  
  7.         continue;  
  8.     }  
  9.   
  10.     lexer.nextToken(JSONToken.COMMA);  
  11.     if (lexer.token() == JSONToken.RBRACE) {  
  12.         lexer.nextToken(JSONToken.COMMA);  
  13.         try {  
  14.             Object instance = null;  
  15.             ObjectDeserializer deserializer = this.config.getDeserializer(clazz);  
  16.             if (deserializer instanceof ASMJavaBeanDeserializer) {  
  17.                 instance = ((ASMJavaBeanDeserializer) deserializer).createInstance(this, clazz);  
  18.             } else if (deserializer instanceof JavaBeanDeserializer) {  
  19.                 instance = ((JavaBeanDeserializer) deserializer).createInstance(this, clazz);  
  20.             }  
  21.   
  22.             if (instance == null) {  
  23.                 if (clazz == Cloneable.class) {  
  24.                     instance = new HashMap();  
  25.                 } else {  
  26.                     instance = clazz.newInstance();  
  27.                 }  
  28.             }  
  29.   
  30.             return instance;  
  31.         } catch (Exception e) {  
  32.             throw new JSONException("create instance error", e);  
  33.         }  
  34.     }  
3,4步的操作:

[java] view plain copy
  1. Object value;  
  2.                 if (ch == '"') {  
  3.                     lexer.scanString();  
  4.                     String strValue = lexer.stringVal();  
  5.                     value = strValue;  
  6.   
  7.                     if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {  
  8.                         JSONScanner iso8601Lexer = new JSONScanner(strValue);  
  9.                         if (iso8601Lexer.scanISO8601DateIfMatch()) {  
  10.                             value = iso8601Lexer.getCalendar().getTime();  
  11.                         }  
  12.                         iso8601Lexer.close();  
  13.                     }  
  14.   
  15.                     object.put(key, value);  
固定模式为,先scan扫描值,在取出后面紧挨着的值。JSON解析json字符类图:

先码后看 FastJSON源码浅析 侵立删

然后到了操作的最后几步,断点调试如下:

先码后看 FastJSON源码浅析 侵立删


先码后看 FastJSON源码浅析 侵立删

以上就是对于fastjson的简单分析,做任何事都要抱着好奇的心态,这样就会有更多的收获的。