1、简介
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
1.1 JSON 语法规则
JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。 JSON是一个序列化的对象或数组。
- 六个构造字符:
- begin-array [ 左方括号
- begin-object { 左大括号
- end-array ] 右方括号
- end-object } 右大括号
- name-separator : 冒号
- value-separator , 逗号
- 在这六个构造字符的前或后允许存在无意义的空白符: 空格、回车换行(\n),换页(\f),制表符(\t),回车(\r)
- 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格式特点对应
- Serialization:序列化,使Java对象到Json字符串的过程。
- Deserialization:反序列化,字符串转换成Java对象。
- 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格式数据的库;它可以分为
- json数据、键值对(值)的转换
- class 和 键值对 转换、 集合数据和值转换
- 整体过程封装代理
2.1 整体架构
java对象到json数据java对象通过TypeAdapter适配器写到JsonWriter中,JsonWriter负责具体的格式书写
json数据到java对象 json数据通过进行包装,具体由JsonReader执行识别操作,通过TypeAdapter适配生成类对象关键类
TypeAdapter类 适配器作用,类和json解析出数据的桥梁;其中包括对象生成,成员变量解析、赋值
JsonWriter 负责json格式字符串生成
JsonReader 负责json格式字符串,解析成键值对或者单个值
从代码角度来说使用了适配器模式,这个是核心模式;而其中又参杂了建造者模式、静态代理模式、策略模式、抽象工程模式等;有兴趣的可以具体去找找这些模式
2.2 JsonReader
按照json格式规则进行读取
- 数组、集合,每次读取的都是单个元素
- 开始读取: beginArray,并略过[
- 读取: nextXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
- 结束读取: endArray, 并略过]
- map、对象按照元素键值对来读取
- 开始读取: beginObject ,并略过{
- 首先读取key: nextName
- 然后读取: nextXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
- 结束读取: endObject,并略过}
其它一些方法,比如略过标记符、空白符等
2.3 JsonWriter
按照json格式规则进行写入
- 数组、集合,每次写入的都是单个元素
- 开始写入: beginArray,并写入[
- 写入: valueXXX系列,xxx基本类型;其它类型可以是array(按照1继续),也可以对象或者map(按照2继续)
- 结束写入: endArray, 并写入]
- 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;
这两个方法实现了具体类型的读取写入过程
这个可以分为以下几种
- TypeAdapters中定义的单个元素类型,包括基本类型、Enum、特殊的基本类型、日期、网络等相关类
- map类型,由工厂类MapTypeAdapterFactory来创建
- 集合或者数组,由工厂类CollectionTypeAdapterFactory来创建
- 用户定义的java bean类,由工厂类ReflectiveTypeAdapterFactory来创建
- 注解? 由工厂类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中,比较多,就不列出来了,大致流程:
- 通过反射获取key、value类型
- 获取key、value对应的TypeAdapter
- 通过各自的adapter进行读写
源码中发现,其读取标签可以是{,[;也就是说这两中都是有效的
2.4.3 java bean类型
代码在ReflectiveTypeAdapterFactory中,比较多,就不列出来了,大致流程:
- 初始化TypeAdapter时,首先得到成员变量名字、值信息集合,并对field进行打开隐私可见
- 名字是string对应的TypeAdapter、value通过类型获取对应的TypeAdapter
- 按照各自的TypeAdapter,按照key-value进行读写
2.4.4 集合、数组类型
代码在CollectionTypeAdapterFactory中,比较多,就不列出来了,大致流程
- 获取元素对应的TypeAdapter
- 通过元素的TypeAdapter进行读写
2.5 惊喜小发现
ObjectConstructor接口 数组、集合、map、普通类生成实例的
Excluder:成员变量是否被序列化,反序列化
FieldNamingStrategy 成员变量名字处理,不包括注解的名字
LongSerializationPolicy,序列化值类
3、结语
文章中对具体TypeAdatpter并没有进行详细的代码流程分析;JsonReader,JsonWriter也没有进行详细的代码流程分析;这是因为,对于框架或者库的理解,首先要理解流程和原理,然后才是细节的处理;作者在这里只想作为进入Gson的辅助文章;更多的细节内容是硬知识,需要自己花费功夫去理解和记忆。
技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给与关注和点赞;如果文章存在错误,也请多多指教!