网络编程-Gson源码分析

1,026 阅读7分钟

1、简介

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

1.1 JSON 语法规则

JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。 JSON是一个序列化的对象或数组。

  1. 六个构造字符:
  • begin-array [ 左方括号
  • begin-object { 左大括号
  • end-array ] 右方括号
  • end-object } 右大括号
  • name-separator : 冒号
  • value-separator , 逗号
  1. 在这六个构造字符的前或后允许存在无意义的空白符: 空格、回车换行(\n),换页(\f),制表符(\t),回车(\r)
  2. JSON的值:
  • 可以是对象、数组、数字、字符串或者三个字面值(false、null、true)中的一个。值中的字面值中的英文必须使用小写。
  • 对象由花括号括起来的逗号分割的成员构成,成员是字符串为key的键值对组成,键值对之间由逗号分隔
  • 数组是由方括号括起来的一组值构成
  • 字符串与C或者Java的字符串非常相似。字符串是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
  • 数字也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

语法规则,是解析和生成的主要依据,Gson库在JsonReader类依据此来解析数据的,在JsonWriter中依据这个来写数据的

1.2 Gson库

是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象;主要有以下概念,这些概念与gson格式特点对应

  1. Serialization:序列化,使Java对象到Json字符串的过程。
  2. Deserialization:反序列化,字符串转换成Java对象。
  3. JSON数据中的JsonElement有下面这四种类型:
  • JsonPrimitive: 例如一个字符串或整型
  • JsonObject: 以JsonElement为键值对集合。key值为string类型
  • JsonArray: JsonElement 的集合。注意数组的元素可以是四种类型中的任意一种,或者混合类型都支持。
  • JsonNull: 值为null

引入

dependencies {
    implementation 'com.google.code.gson:gson:x.x.x'
}

常用使用

// 使用new方法
Gson gson = new Gson();

// toJson 将bean对象转换为json字符串
String jsonStr = gson.toJson(user, Xxx.class);

// fromJson 将json字符串转为bean对象
Student user= gson.fromJson(jsonStr, Xxx.class);

2、Gson源码分析

这是一个序列化、反序列化json格式数据的库;它可以分为

  1. json数据、键值对(值)的转换
  2. class 和 键值对 转换、 集合数据和值转换
  3. 整体过程封装代理

2.1 整体架构

java对象到json数据

java对象通过TypeAdapter适配器写到JsonWriter中,JsonWriter负责具体的格式书写

json数据到java对象 json数据通过进行包装,具体由JsonReader执行识别操作,通过TypeAdapter适配生成类对象

关键类

TypeAdapter类 适配器作用,类和json解析出数据的桥梁;其中包括对象生成,成员变量解析、赋值

JsonWriter 负责json格式字符串生成

JsonReader 负责json格式字符串,解析成键值对或者单个值

从代码角度来说使用了适配器模式,这个是核心模式;而其中又参杂了建造者模式、静态代理模式、策略模式、抽象工程模式等;有兴趣的可以具体去找找这些模式

2.2 JsonReader

按照json格式规则进行读取

  1. 数组、集合,每次读取的都是单个元素
  • 开始读取: beginArray,并略过[
  • 读取: nextXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
  • 结束读取: endArray, 并略过]
  1. map、对象按照元素键值对来读取
  • 开始读取: beginObject ,并略过{
  • 首先读取key: nextName
  • 然后读取: nextXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
  • 结束读取: endObject,并略过}

其它一些方法,比如略过标记符、空白符等

2.3 JsonWriter

按照json格式规则进行写入

  1. 数组、集合,每次写入的都是单个元素
  • 开始写入: beginArray,并写入[
  • 写入: valueXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
  • 结束写入: endArray, 并写入]
  1. map、对象按照元素键值对来写入
  • 开始写入: beginObject, 并写入{
  • 首先准备key: name
  • 然后写入: valueXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
  • 结束写入: endObject,并写入}

还有一些按照数据类别写入标记符,空白符

2.4 TypeAdapter

抽象类,其内部方法fromJson、toJson等方法通过read、write抽象方法来实现

public abstract void write(JsonWriter out, T value) throws IOException;

public abstract T read(JsonReader in) throws IOException;

这两个方法实现了具体类型的读取写入过程

这个可以分为以下几种

  1. TypeAdapters中定义的单个元素类型,包括基本类型、Enum、特殊的基本类型、日期、网络等相关类
  2. map类型,由工厂类MapTypeAdapterFactory来创建
  3. 集合或者数组,由工厂类CollectionTypeAdapterFactory来创建
  4. 用户定义的java bean类,由工厂类ReflectiveTypeAdapterFactory来创建
  5. 注解? 由工厂类JsonAdapterAnnotationTypeAdapterFactory来创建,其内部由其它工厂来区分创建

在这里,我们就分析下前四种,后面一种,由于没有用过,不是清楚什么类型,理解起来有点难度;

2.4.1 TypeAdapters中的‘基本类型’

这些是单值类型,作为数组或者集合的元素;也可以作为map或者java bean中的value类型,如果是字符串类型,也可以作为key类型

Class类型,json数据不接受;read,write实现直接报异常

    public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
    @Override
    public void write(JsonWriter out, Class value) throws IOException {
      throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
              + value.getName() + ". Forgot to register a type adapter?");
    }
    @Override
    public Class read(JsonReader in) throws IOException {
      throw new UnsupportedOperationException(
              "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
    }
  }.nullSafe();

String类型,代码就是把字符串读出,写入

public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
    @Override
    public StringBuilder read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      return new StringBuilder(in.nextString());
    }
    @Override
    public void write(JsonWriter out, StringBuilder value) throws IOException {
      out.value(value == null ? null : value.toString());
    }
  };

URL数据,写入时,写的是String,读取时也是String,不过再加了转换

    public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
    @Override
    public URL read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      String nextString = in.nextString();
      return "null".equals(nextString) ? null : new URL(nextString);
    }
    @Override
    public void write(JsonWriter out, URL value) throws IOException {
      out.value(value == null ? null : value.toExternalForm());
    }
  };

其它也有一些,都是通过转化成基本数据或者string类型来读取和写入的;

2.4.2 map类型

代码在MapTypeAdapterFactory中,比较多,就不列出来了,大致流程:

  1. 通过反射获取key、value类型
  2. 获取key、value对应的TypeAdapter
  3. 通过各自的adapter进行读写

源码中发现,其读取标签可以是{,[;也就是说这两中都是有效的

2.4.3 java bean类型

代码在ReflectiveTypeAdapterFactory中,比较多,就不列出来了,大致流程:

  1. 初始化TypeAdapter时,首先得到成员变量名字、值信息集合,并对field进行打开隐私可见
  2. 名字是string对应的TypeAdapter、value通过类型获取对应的TypeAdapter
  3. 按照各自的TypeAdapter,按照key-value进行读写

2.4.4 集合、数组类型

代码在CollectionTypeAdapterFactory中,比较多,就不列出来了,大致流程

  1. 获取元素对应的TypeAdapter
  2. 通过元素的TypeAdapter进行读写

2.5 惊喜小发现

ObjectConstructor接口 数组、集合、map、普通类生成实例的

Excluder:成员变量是否被序列化,反序列化

FieldNamingStrategy 成员变量名字处理,不包括注解的名字

LongSerializationPolicy,序列化值类

3、结语

文章中对具体TypeAdatpter并没有进行详细的代码流程分析;JsonReader,JsonWriter也没有进行详细的代码流程分析;这是因为,对于框架或者库的理解,首先要理解流程和原理,然后才是细节的处理;作者在这里只想作为进入Gson的辅助文章;更多的细节内容是硬知识,需要自己花费功夫去理解和记忆。

技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给与关注和点赞;如果文章存在错误,也请多多指教!