fastjson深度源码解析- 序列化(一) - 序列化基础类型解析

x33g5p2x  于2021-12-25 转载在 其他  
字(5.9k)|赞(0)|评价(0)|浏览(636)

首先感谢高铁同学同意我撰写fastjson源码解析并且给予了必要的指导,fastjson是一个高效的json与java对象序列化框架,很多公司和开源框架都从fastjson中受益。

目前网上公开的fastjson源码解析太少或者缺少深度,因此我计划通过研究源码的方式并记录下来,让更多想了解底层实现的同学受益。如果在阅读过程中发现错误,欢迎与我沟通 。

  1. 邮箱:jason.shang@hotmail.com
  2. 微信:skyingshang

可以在线阅读gitbookfastjson源码解析,也可以参考已经添加注释的源码fastjson注释版本_master分支

我在工作之余编写源码解析的目的:
  1. 做技术应该追求极致和细节,让更多的人拥抱开源并从中受益
  2. 深入理解fastjson作者的设计思想
  3. 掌握基本的词法和语法分析实现
  4. 源码是最好的教材,降低阅读开源代码的成本
  5. 巩固技术基础
  6. 分享是一种美德
为了尊重作者的劳动,如果您转载请保留以下内容:
  1. 文章作者 诣极(商宗海)
  2. 框架作者 高铁
  3. 文章地址 https://zonghaishang.gitbooks.io/fastjson-source-code-analysis/content/
  4. 代码地址 https://github.com/zonghaishang/fastjson
  5. 框架地址 https://github.com/alibaba/fastjson

概要

fastjson核心功能包括序列化和反序列化,序列化的含义是将java对象转换成跨语言的json字符串。我认为从这里作为分析入口相对比较简单,第二章会从反序列化角度切入,会包含词法分析等较为复杂点展开。

现在,我们正式开始咀嚼原汁原味的代码吧,我添加了详细的代码注释。

SerializeWriter成员变量

com.alibaba.fastjson.serializer.SerializeWriter类非常重要,序列化输出都是通过转换底层操作,重要字段如下:

  1. /** 字符类型buffer */
  2. private final static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
  3. /** 字节类型buffer */
  4. private final static ThreadLocal<byte[]> bytesBufLocal = new ThreadLocal<byte[]>();
  5. /** 存储序列化结果buffer */
  6. protected char buf[];
  7. /** buffer中包含的字符数 */
  8. protected int count;
  9. /** 序列化的特性,比如写枚举按照名字还是枚举值 */
  10. protected int features;
  11. /** 序列化输出器 */
  12. private final Writer writer;
  13. /** 是否使用单引号输出json */
  14. protected boolean useSingleQuotes;
  15. /** 输出字段是否追加 "和:字符 */
  16. protected boolean quoteFieldNames;
  17. /** 是否对字段排序 */
  18. protected boolean sortField;
  19. /** 禁用字段循环引用探测 */
  20. protected boolean disableCircularReferenceDetect;
  21. protected boolean beanToArray;
  22. /** 按照toString方式获取对象字面值 */
  23. protected boolean writeNonStringValueAsString;
  24. /** 如果字段默认值不输出,比如原型int,默认值0不输出 */
  25. protected boolean notWriteDefaultValue;
  26. /** 序列化枚举时使用枚举name */
  27. protected boolean writeEnumUsingName;
  28. /** 序列化枚举时使用枚举toString值 */
  29. protected boolean writeEnumUsingToString;
  30. protected boolean writeDirect;
  31. /** key分隔符,默认单引号是',双引号是" */
  32. protected char keySeperator;
  33. protected int maxBufSize = -1;
  34. protected boolean browserSecure;
  35. protected long sepcialBits;

SerializeWriter成员函数

序列化整形数字

  1. public void writeInt(int i) {
  2. /** 如果是整数最小值,调用字符串函数输出到缓冲区*/
  3. if (i == Integer.MIN_VALUE) {
  4. write("-2147483648");
  5. return;
  6. }
  7. /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */
  8. int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);
  9. int newcount = count + size;
  10. /** 如果当前存储空间不够 */
  11. if (newcount > buf.length) {
  12. if (writer == null) {
  13. /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
  14. expandCapacity(newcount);
  15. } else {
  16. char[] chars = new char[size];
  17. /** 将整数i转换成单字符并存储到chars数组 */
  18. IOUtils.getChars(i, size, chars);
  19. /** 将chars字符数组内容写到buffer中*/
  20. write(chars, 0, chars.length);
  21. return;
  22. }
  23. }
  24. /** 如果buffer空间够,直接将字符写到buffer中 */
  25. IOUtils.getChars(i, newcount, buf);
  26. /** 重新计数buffer中字符数 */
  27. count = newcount;
  28. }

其中值得提一下的是IOUtils.getChars,里面利用了Integer.getChars(int i, int index, char[] buf),主要的思想是整数超过65536 进行除以100, 循环取出数字后两位,依次将个位和十位转换为单字符,如果整数小于等于65536,进行除以10,取出个位数字并转换单字符,getCharts中 q = (i * 52429) >>> (16+3),可以理解为 (i乘以0.1), 但是精度更高。

序列化长整形数字

  1. public void writeLong(long i) {
  2. boolean needQuotationMark = isEnabled(SerializerFeature.BrowserCompatible) //
  3. && (!isEnabled(SerializerFeature.WriteClassName)) //
  4. && (i > 9007199254740991L || i < -9007199254740991L);
  5. if (i == Long.MIN_VALUE) {
  6. if (needQuotationMark) write("\"-9223372036854775808\"");
  7. /** 如果是长整数最小值,调用字符串函数输出到缓冲区*/
  8. else write("-9223372036854775808");
  9. return;
  10. }
  11. /** 根据数字判断占用的位数,负数会多一位用于存储字符`-` */
  12. int size = (i < 0) ? IOUtils.stringSize(-i) + 1 : IOUtils.stringSize(i);
  13. int newcount = count + size;
  14. if (needQuotationMark) newcount += 2;
  15. /** 如果当前存储空间不够 */
  16. if (newcount > buf.length) {
  17. if (writer == null) {
  18. /** 扩容到为原有buf容量1.5倍+1, copy原有buf的字符*/
  19. expandCapacity(newcount);
  20. } else {
  21. char[] chars = new char[size];
  22. /** 将长整数i转换成单字符并存储到chars数组 */
  23. IOUtils.getChars(i, size, chars);
  24. if (needQuotationMark) {
  25. write('"');
  26. write(chars, 0, chars.length);
  27. write('"');
  28. } else {
  29. write(chars, 0, chars.length);
  30. }
  31. return;
  32. }
  33. }
  34. /** 添加引号 */
  35. if (needQuotationMark) {
  36. buf[count] = '"';
  37. IOUtils.getChars(i, newcount - 1, buf);
  38. buf[newcount - 1] = '"';
  39. } else {
  40. IOUtils.getChars(i, newcount, buf);
  41. }
  42. count = newcount;
  43. }

序列化长整型和整型非常类似,增加了双引号判断,采用用了和Integer转换为单字符同样的技巧。

序列化浮点类型数字

  1. public void writeDouble(double doubleValue, boolean checkWriteClassName) {
  2. /** 如果doubleValue不合法或者是无穷数,调用writeNull */
  3. if (Double.isNaN(doubleValue)
  4. || Double.isInfinite(doubleValue)) {
  5. writeNull();
  6. } else {
  7. /** 将高精度double转换为字符串 */
  8. String doubleText = Double.toString(doubleValue);
  9. /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */
  10. if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && doubleText.endsWith(".0")) {
  11. doubleText = doubleText.substring(0, doubleText.length() - 2);
  12. }
  13. /** 调用字符串输出方法 */
  14. write(doubleText);
  15. /** 如果开启序列化WriteClassName特性,输出Double类型 */
  16. if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {
  17. write('D');
  18. }
  19. }
  20. }
  21. public void writeFloat(float value, boolean checkWriteClassName) {
  22. /** 如果value不合法或者是无穷数,调用writeNull */
  23. if (Float.isNaN(value) //
  24. || Float.isInfinite(value)) {
  25. writeNull();
  26. } else {
  27. /** 将高精度float转换为字符串 */
  28. String floatText= Float.toString(value);
  29. /** 启动WriteNullNumberAsZero特性,会将结尾.0去除 */
  30. if (isEnabled(SerializerFeature.WriteNullNumberAsZero) && floatText.endsWith(".0")) {
  31. floatText = floatText.substring(0, floatText.length() - 2);
  32. }
  33. write(floatText);
  34. /** 如果开启序列化WriteClassName特性,输出float类型 */
  35. if (checkWriteClassName && isEnabled(SerializerFeature.WriteClassName)) {
  36. write('F');
  37. }
  38. }
  39. }

序列化浮点类型的基本思路是先转换为字符串,然后在输出到输出流中。

序列化枚举类型

  1. public void writeEnum(Enum<?> value) {
  2. if (value == null) {
  3. /** 如果枚举value为空,调用writeNull输出 */
  4. writeNull();
  5. return;
  6. }
  7. String strVal = null;
  8. /** 如果开启序列化输出枚举名字作为属性值 */
  9. if (writeEnumUsingName && !writeEnumUsingToString) {
  10. strVal = value.name();
  11. } else if (writeEnumUsingToString) {
  12. /** 采用枚举默认toString方法作为属性值 */
  13. strVal = value.toString();;
  14. }
  15. if (strVal != null) {
  16. /** 如果开启引号特性,输出json包含引号的字符串 */
  17. char quote = isEnabled(SerializerFeature.UseSingleQuotes) ? '\'' : '"';
  18. write(quote);
  19. write(strVal);
  20. write(quote);
  21. } else {
  22. /** 输出枚举所在的索引号 */
  23. writeInt(value.ordinal());
  24. }
  25. }

序列化单字符

  1. public void write(int c) {
  2. int newcount = count + 1;
  3. /** 如果当前存储空间不够 */
  4. if (newcount > buf.length) {
  5. if (writer == null) {
  6. expandCapacity(newcount);
  7. } else {
  8. /** 强制流输出并刷新缓冲区 */
  9. flush();
  10. newcount = 1;
  11. }
  12. }
  13. /** 存储单字符到buffer并更新计数 */
  14. buf[count] = (char) c;
  15. count = newcount;
  16. }

序列化Null

  1. public void writeNull() {
  2. /** 调用输出字符串null */
  3. write("null");
  4. }

序列化Boolean

  1. public void write(boolean value) {
  2. if (value) {
  3. /** 输出true字符串 */
  4. write("true");
  5. } else {
  6. /** 输出false字符串 */
  7. write("false");
  8. }
  9. }

相关文章