追踪解析Gson源码(2)
接上篇
三 JsonReader 和 JsonWriter
在进行 json 的序列化和反序列化源码解析之前先了解一下其主体工具类。
1 JsonReader
JsonReader 是 Gson 中用于 json 反序列化的主体。
在 Gson 包中可以不使用 Gson 门面而单独使用 JsonReader 进行 json 的反序列化:
public static void main(String[] args){ //json 字符串 String json = "{\"name\":\"zhangsan\",\"age\":18}"; //使用 StringReader 包装字符串 StringReader strReader = new StringReader(json); //使用 JsonReader 包装 StringReader JsonReader jsonReader = new JsonReader(strReader); //创建一个参数均为空的目标对象 Person p = new Person(); try { //beginObject(...) 方法用于告诉 jsonReader 开始读取一个 object jsonReader.beginObject(); //while 循环读取 while(jsonReader.hasNext()){ //读取一个 json 的 key 值 String name = jsonReader.nextName(); switch (name){ case "name": p.setName(jsonReader.nextString()); //读取一个 String 作为 value,存入对象中 break; case "age": p.setAge(jsonReader.nextInt()); //读取一个 int 作为 value,存入对象中 break; } } jsonReader.endObject(); } catch (IOException e) { e.printStackTrace(); } System.out.println("名字:" + p.getName()); System.out.println("年龄:" +p.getAge()) }
JsonReader 的构造方法:
//JsonReader.class public JsonReader(Reader in) { if (in == null) { throw new NullPointerException("in == null"); } //传入的 Reader 中保存着要读取的 json 主体 //此处比较常用的是 StringReader,但是也可以是 FileReader 等其它 Reader 接口的实现类 this.in = in; }
JsonReader 仅此一个构造方法,创建对象均调用此方法。
先来看一下 JsonReader 中非常重要的一个非公开方法 doPeek():
//JsonReader.class int doPeek() throws IOException { //stack 是一个定义在 JsonReader 中的 int 数组,作为 JsonReader 的指令集存在,用于控制变量 peeked 的状态 //在 JsonReader 初始化的时候会将 stack 的第一个元素变成6,其余均为0 //6的意思根据官方注释为 "No object or array has been started"(还没开始读取对象或列表) //6作为常量保存在 JsonScope 中,JsonScope 中还保存了很多代表指令的常量,下列会用到 //stackSize 是 stack 的有效元素计数器,初始化时 stackSize = 1,即只有第一个元素是有效的 int peekStack = stack[stackSize - 1]; //JsonScope.EMPTY_ARRAY = 1 if (peekStack == JsonScope.EMPTY_ARRAY) { //JsonScope.NONEMPTY_ARRAY = 2 stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY; } else if (peekStack == JsonScope.NONEMPTY_ARRAY) { //在第一次调用 nextNonWhitespace(true) 方法的时候,json 字符串会被转存为一个 char 数组 //该方法以 int 值的形式返回下一个要解析的 char 对象 int c = nextNonWhitespace(true); switch (c) { case ']': //peeked 是 JsonReader 中最重要的用来状态控制的 int 变量 //peeker 和 stack 会协同控制 JsonReader 的逻辑行为 return peeked = PEEKED_END_ARRAY; //PEEKED_END_ARRAY = 4 case ';': //检查标准协议选项,json 标准中的符号没有分号 //所以在 lenient = false 的时候就会报错 checkLenient(); case ',': break; default: throw syntaxError("Unterminated array"); } //JsonScope.EMPTY_OBJECT = 3,JsonScope.NONEMPTY_OBJECT = 5 } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) { //JsonScope.DANGLING_NAME = 4 stack[stackSize - 1] = JsonScope.DANGLING_NAME; if (peekStack == JsonScope.NONEMPTY_OBJECT) { int c = nextNonWhitespace(true); switch (c) { case '}': return peeked = PEEKED_END_OBJECT; //PEEKED_END_OBJECT = 2 case ';': checkLenient(); case ',': break; default: throw syntaxError("Unterminated object"); } } int c = nextNonWhitespace(true); switch (c) { case '"': return peeked = PEEKED_DOUBLE_QUOTED_NAME; //PEEKED_DOUBLE_QUOTED_NAME = 13 case '\'': checkLenient(); return peeked = PEEKED_SINGLE_QUOTED_NAME; //PEEKED_SINGLE_QUOTED_NAME = 12 case '}': if (peekStack != JsonScope.NONEMPTY_OBJECT) { return peeked = PEEKED_END_OBJECT; } else { throw syntaxError("Expected name"); } default: checkLenient(); pos--; if (isLiteral((char) c)) { return peeked = PEEKED_UNQUOTED_NAME; //PEEKED_UNQUOTED_NAME = 14 } else { throw syntaxError("Expected name"); } } } else if (peekStack == JsonScope.DANGLING_NAME) { stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT; int c = nextNonWhitespace(true); switch (c) { case ':': break; case '=': checkLenient(); //buffer 是储存 json 字符串的 char 数组 //pos 是已经读取到字符的数量指针 //limit 是 buffer 的可用部分的总长 if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') { pos++; } break; default: throw syntaxError("Expected ':'"); } //JsonScope.EMPTY_DOCUMENT = 6 //第一次进入方法的时候,会进入这个 if 语句中 } else if (peekStack == JsonScope.EMPTY_DOCUMENT) { //检查标准化协议相关 if (lenient) { consumeNonExecutePrefix(); } //JsonScope.NONEMPTY_DOCUMENT = 7 stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT; } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) { int c = nextNonWhitespace(false); if (c == -1) { return peeked = PEEKED_EOF; } else { checkLenient(); pos--; } //JsonScope.CLOSED = 8 } else if (peekStack == JsonScope.CLOSED) { throw new IllegalStateException("JsonReader is closed"); } //在这里获取到了下一个要解析的 char 的 int 值 int c = nextNonWhitespace(true); //进入 switch 去进行定位,定位到了之后修改 peeked 的状态 switch (c) { case ']': if (peekStack == JsonScope.EMPTY_ARRAY) { return peeked = PEEKED_END_ARRAY; } case ';': case ',': if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) { checkLenient(); pos--; return peeked = PEEKED_NULL; } else { throw syntaxError("Unexpected value"); } case '\'': checkLenient(); return peeked = PEEKED_SINGLE_QUOTED; case '"': return peeked = PEEKED_DOUBLE_QUOTED; case '[': return peeked = PEEKED_BEGIN_ARRAY; case '{': return peeked = PEEKED_BEGIN_OBJECT; default: pos--; } //peekKeyword() 方法会从 buffer 数组里获取下一个 char //然后根据这个字符判断下一个要处理的字符串是不是 true、false、null 等特殊字符 //如果不是,会返回 result = PEEKED_NONE int result = peekKeyword(); if (result != PEEKED_NONE) { //不等于 PEEKED_NONE,证明下一个确实是特殊字符 return result; } //peekNumber() 方法和上述 peekKeyword() 方法很类似 //用于判断下一个要处理的字符串是否是数字 result = peekNumber(); if (result != PEEKED_NONE) { return result; } //isLiteral(buffer[pos]) 用于判断下一个字符是否是特殊符 //比如 换行符、井号、括号 等 //如果是 换行符 的话这里就会抛出错误 if (!isLiteral(buffer[pos])) { throw syntaxError("Expected value"); } checkLenient(); return peeked = PEEKED_UNQUOTED; //PEEKED_UNQUOTED = 10 }
方法虽然比较长,但是代码其实比较简单,本质上是根据 stack 指令去修改 peeked 的值,从而达到状态控制的效果。
再来看 beginObject() 方法:
//JsonReader.class public void beginObject() throws IOException { int p = peeked; //初始化时 peeked = PEEKED_NONE //在 doPeek() 方法中会修改成 PEEKED_BEGIN_OBJECT,即开始一个 Object 的序列化 if (p == PEEKED_NONE) { p = doPeek(); } if (p == PEEKED_BEGIN_OBJECT) { //push(...) 方法会检查 stack 数组的容积,适时进行扩容,并把传入的指令存放到数组中 //此处将 EMPTY_OBJECT 指令存入到 stack 中 push(JsonScope.EMPTY_OBJECT); //将 peeked 状态初始化 peeked = PEEKED_NONE; } else { throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString()); } }
再来看 nextName() 方法:
//JsonReader.class public String nextName() throws IOException { //老样子进行 peeked 的状态获取 int p = peeked; if (p == PEEKED_NONE) { p = doPeek(); } String result; //在这里通过 if 语句和 peeked 定位 json 的 key 是用单引号还是双引号包裹的 //result 就是 key 的字符串 if (p == PEEKED_UNQUOTED_NAME) { result = nextUnquotedValue(); } else if (p == PEEKED_SINGLE_QUOTED_NAME) { result = nextQuotedValue('\''); } else if (p == PEEKED_DOUBLE_QUOTED_NAME) { result = nextQuotedValue('"'); } else { throw new IllegalStateException("Expected a name but was " + peek() + locationString()); } //将 peeked 状态初始化 peeked = PEEKED_NONE; //pathNames 是一个用来储存所有 key 的字符串的数组 pathNames[stackSize - 1] = result; return result; }
nextQuotedValue(...) 方法里实际上是比较繁琐的字符串处理,在这里暂时不展开了。
hasNext() 和 nextString() 方法其实都和 nextName() 方法差不多,本质上都是根据 peeked 的值去进入不同的 if 语句来处理字符串。
2 JsonWriter
JsonWriter 是 Gson 中用于 json 序列化的主体。
和 JsonReader 一样,也可以不使用 Gson 门面而单独使用 JsonWriter 进行 json 的序列化:
public static void main(String[] args){ //组装 bean Person person = new Person(); person.setName("zhangsan"); person.setAge(18); //创建一个 StringWriter,本质是 StringBuffer 的封装 StringWriter writer = new StringWriter(); //用 JsonWriter 去封装 StringWriter JsonWriter jsonWriter = new JsonWriter(writer); try { //启动一个 object 的写入 jsonWriter.beginObject(); //写入 key-value jsonWriter.name("name").value(person.getName()); jsonWriter.name("age").value(person.getAge()); //结束命令 jsonWriter.endObject(); //将 JsonWriter 里的数据存入到 StringWriter 中 jsonWriter.flush(); } catch (IOException e) { e.printStackTrace(); } System.out.println(writer.toString()); }
JsonWriter 的构造方法:
//JsonWriter.class public JsonWriter(Writer out) { if (out == null) { throw new NullPointerException("out == null"); } this.out = out; }
追踪一下 beginObject() 方法:
//JsonWriter.class public JsonWriter beginObject() throws IOException { //这个方法主要的作用是在不同的 object 之间加逗号 //如果是起始第一个 object 就不需要了,会直接跳过 writeDeferredName(); return open(EMPTY_OBJECT, "{"); }
继续追踪 open(...) 方法:
//JsonWriter.class private JsonWriter open(int empty, String openBracket) throws IOException { //该方法会使用 stack 中最新的指令进行操作 beforeValue(); //push(...) 方法会检查 stack 数组的容积,适时进行扩容,并把传入的指令存放到数组中 push(empty); //写入字符串 out.write(openBracket); return this; }
JsonWriter 中没有 JsonReader 中那么复杂的指令操作,所以没有引入 peeked 变量,仅仅使用 stack 数组来控制状态。
stack 的控制封装在 beforeValue() 方法中:
//JsonWriter.class private void beforeValue() throws IOException { //peek() 方法会根据 stack 数组的指令获取到下一个要操作的字符类型 switch (peek()) { case NONEMPTY_DOCUMENT: if (!lenient) { throw new IllegalStateException( "JSON must have only one top-level value."); } case EMPTY_DOCUMENT: //replaceTop(...) 方法会将 stack 最新的指令更新成传入的参数 replaceTop(NONEMPTY_DOCUMENT); break; case EMPTY_ARRAY: replaceTop(NONEMPTY_ARRAY); //换行 newline(); break; case NONEMPTY_ARRAY: out.append(','); newline(); break; case DANGLING_NAME: //separator 即为 ":" (冒号) out.append(separator); replaceTop(NONEMPTY_OBJECT); break; default: throw new IllegalStateException("Nesting problem."); } }
来看一下 name(...) 方法:
//JsonWriter.class public JsonWriter name(String name) throws IOException { if (name == null) { throw new NullPointerException("name == null"); } if (deferredName != null) { throw new IllegalStateException(); } if (stackSize == 0) { throw new IllegalStateException("JsonWriter is closed."); } //这一行代码中会暂时将传入的 name 参数保存在一个全局变量中, //所以 JsonWriter 在调用 value(...) 方法之前不能再调用 name(...) 方法了,不然在上方的判断中会报错 deferredName = name; return this; }
来看一下 value(...) 方法:
//JsonWriter.class public JsonWriter value(String value) throws IOException { if (value == null) { //nullValue() 方法会在 value 值的地方存入一个 null return nullValue(); } //这一方法会将 之前保存在 deferredName 中的字符串写入到 writer 中 //方法中会处理加逗号、将 deferredName 变量清空等问题 //核心是调用 string(...) 方法写入 writeDeferredName(); //更新 stack 指令 beforeValue(); //和上方写入 deferredName 一样,此处调用写入 value string(value); return this; }
总的来说 JsonWriter 比 JsonReader 简单,简要描述一下不过多展开。
四 JSON 字符串转 Bean
该 part 的起点:
Person person = gson.fromJson(json,Person.class);
追踪 fromJson(...) 方法:
//Gson.class public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException { //将字符串转成 object 的主体方法 //3.1 Object object = fromJson(json, (Type) classOfT); return Primitives.wrap(classOfT).cast(object); }
先来看一下上述的第二行代码:
return Primitives.wrap(classOfT).cast(object);
Primitives.wrap(...) 方法的实现:
//Primitives.class public static <T> Class<T> wrap(Class<T> type) { //PRIMITIVE_TO_WRAPPER_TYPE 是一个 map 对象 //$Gson$Preconditions.checkNotNull(...) 用来效验 type 不为空 @SuppressWarnings("unchecked") Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get( $Gson$Preconditions.checkNotNull(type)); //如果 map 中不存在 type 为 key 的值,则返回 type,否则返回取到的 value return (wrapped == null) ? type : wrapped; }
PRIMITIVE_TO_WRAPPER_TYPE 是一个定义在 Primitives 中的 map 对象:
//Primitives.class private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
PRIMITIVE_TO_WRAPPER_TYPE 中主要存放了 float、int、double 等原始类型的 class:
//Primitives.class static { Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16); Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16); //以下代码是将原始类型和包装类型存入两个 map 的过程 add(primToWrap, wrapToPrim, boolean.class, Boolean.class); add(primToWrap, wrapToPrim, byte.class, Byte.class); add(primToWrap, wrapToPrim, char.class, Character.class); add(primToWrap, wrapToPrim, double.class, Double.class); add(primToWrap, wrapToPrim, float.class, Float.class); add(primToWrap, wrapToPrim, int.class, Integer.class); add(primToWrap, wrapToPrim, long.class, Long.class); add(primToWrap, wrapToPrim, short.class, Short.class); add(primToWrap, wrapToPrim, void.class, Void.class); //Collections.unmodifiableMap(...) 返回一个只能阅读不能修改的 map //原始类型作为 key,包装类型作为 value PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); //包装类型作为 value,原始类型作为 key WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); }
由此可见 Primitives.wrap(...) 本质上是判断传入的 type 是否是原始类型,如果是,则会转换成包装类型并返回。
至于 Primitives.wrap(classOfT).cast(object) 中的 cast(...),则是定义在 Class 中的方法:
//Class.class @HotSpotIntrinsicCandidate public T cast(Object obj) { //isInstance(...) 方法等价于关键词 instanceof //如果 obj 不为 null 且 不为该 Class 对象的子类,则会抛出错误 if (obj != null && !isInstance(obj)) throw new ClassCastException(cannotCastMsg(obj)); return (T) obj; }
代码比较简单,本质上就是强转类型。
再来看这行代码:
Object object = fromJson(json, (Type) classOfT);
追踪具体实现:
//Gson.class public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException { //非 null 判断 if (json == null) { return null; } //StringReader 是一个 jdk 中存在的 String 和 Reader 的关联封装类 StringReader reader = new StringReader(json); //主体功能实现方法 T target = (T) fromJson(reader, typeOfT); //返回一个指定泛型的对象 return target; }
继续追踪重载方法:
//Gson.class public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { //初始化一个 JsonReader JsonReader jsonReader = newJsonReader(json); //主体功能实现方法 T object = (T) fromJson(jsonReader, typeOfT); //在整个反序列化过程结束之前效验 jsonReader 的 peeked 的状态 //如果 peeker 未处于结束状态,则会报错 assertFullConsumption(object, jsonReader); return object; }
继续追踪重载方法:
//Gson.class public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; boolean oldLenient = reader.isLenient(); //打开 reader 的标准化检验 reader.setLenient(true); try { //此处相当于调用了一次 JsonReader 中的 doPeek() 方法 reader.peek(); isEmpty = false; //TypeToken 本质上是 Class 的增强封装类 TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT); //根据 TypeToken 获取对应的能够处理其类型的 TypeAdapter TypeAdapter<T> typeAdapter = getAdapter(typeToken); //反射创建 object T object = typeAdapter.read(reader); return object; } catch (EOFException e) { if (isEmpty) { return null; } throw new JsonSyntaxException(e); } catch (IllegalStateException e) { throw new JsonSyntaxException(e); } catch (IOException e) { throw new JsonSyntaxException(e); } catch (AssertionError e) { throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { reader.setLenient(oldLenient); } }
先来看一下 getAdapter(...) 方法:
//Gson.class public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) { //typeTokenCache 是 Gson 中的一个 map 对象,用于储存 TypeAdapter //typeTokenCache 是一个 Gson 中各个线程公用的一个缓存池 TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); if (cached != null) { //如果本身就有储存了,就直接返回 return (TypeAdapter<T>) cached; } //calls 是一个 ThreadLocal 对象 //ThreadLocal 是 Gson 中单个线程使用的缓存池,在里面存入的对象会在 finally 代码块中清空掉 Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get(); //判断是否需要清空 ThreadLocal boolean requiresThreadLocalCleanup = false; if (threadCalls == null) { threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>(); calls.set(threadCalls); //这里存入了对象,所以需要清空 ThreadLocal requiresThreadLocalCleanup = true; } //如果存在对象,就会在这里取用出来并返回 //FutureTypeAdapter 是一个门面模式的应用,其实本质是使用内部的 TypeAdapter 去处理业务 //如果内部没有存入实际处理业务的 TypeAdapter,就会报错 FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type); if (ongoingCall != null) { return ongoingCall; } try { FutureTypeAdapter<T> call = new FutureTypeAdapter<T>(); threadCalls.put(type, call); //这个方法的主体是这个 for 循环,用于从 Gson 初始化的时候储存的列表中获取到对应的 TypeAdapter for (TypeAdapterFactory factory : factories) { //TypeAdapter 的 create(...) 方法对于不是对应类型的参数会返回 null TypeAdapter<T> candidate = factory.create(this, type); if (candidate != null) { //在此处会存入业务处理的 TypeAdapter call.setDelegate(candidate); //在此处存入公用缓存 typeTokenCache.put(type, candidate); return candidate; } } throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type); } finally { //清除 ThreadLocal 缓存 threadCalls.remove(type); if (requiresThreadLocalCleanup) { calls.remove(); } } }
到此为止 json 的反序列化就基本完毕了.
五 Bean 转 JSON 字符串
该 part 的起点:
String json2 = gson.toJson(person);
追踪 toJson(...) 方法:
//Gson.class public String toJson(Object src) { if (src == null) { return toJson(JsonNull.INSTANCE); } return toJson(src, src.getClass()); }
toJson(JsonNull.INSTANCE) 方法最后会输出一个 null 字符串,不多展开了。
继续来关注下方主要实现逻辑:
//Gson.class public String toJson(Object src, Type typeOfSrc) { //新建一个 StringWriter StringWriter writer = new StringWriter(); //主要逻辑 toJson(src, typeOfSrc, writer); //返回 return writer.toString(); }
继续追踪主要逻辑方法:
//Gson.class public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException { try { //这里将 StringWriter 包装成了 JsonWriter JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); //主要逻辑 toJson(src, typeOfSrc, jsonWriter); } catch (IOException e) { throw new JsonIOException(e); } }
继续追踪主要逻辑方法:
//Gson.class public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { //获取适配的 TypeAdapter TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc)); //下方代码均为储存并存入一些标准化的设置 boolean oldLenient = writer.isLenient(); writer.setLenient(true); boolean oldHtmlSafe = writer.isHtmlSafe(); writer.setHtmlSafe(htmlSafe); boolean oldSerializeNulls = writer.getSerializeNulls(); writer.setSerializeNulls(serializeNulls); //写入 try { ((TypeAdapter<Object>) adapter).write(writer, src); } catch (IOException e) { throw new JsonIOException(e); } catch (AssertionError e) { throw new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage(), e); } finally { //还原 writer.setLenient(oldLenient); writer.setHtmlSafe(oldHtmlSafe); writer.setSerializeNulls(oldSerializeNulls); } }
基本的逻辑都在上面讲过了,不赘述。
到此为止 json 的序列化就基本完毕了。
四 总结
Gson 的代码封装很薄,本身不难,但是为了照顾兼容性,代码中存在很多条件判断,导致代码看上去很繁琐。同时为了兼顾性能做了很多有意思的设计,比如获取适配器的时候的双缓存设计,应该是为了提高解析器的复用效率,具体有待研究。
总结一下 Gson 的基本思路:
· 解析器(Gson)将使用者传入的字符串或对象存入读取器(Reader)或者写入器(Writer)中
· 解析器遍历并获取能够处理对应类型的适配器工厂(TypeAdapterFactory)
· 适配器工厂会创建出对应类型的适配器(TypeAdapter)
· 解析器将阅读器或写入器交给适配器
· 适配器自行通过业务逻辑操作读取器或写入器,输出需要的结果
· 解析器接收此输出,并交给使用者
五 一点唠叨
· Gson 太过强调功能的全面,解析器的初始化非常复杂
· JsonReader 的状态控制太过复杂和精密,笔者到现在也没全部弄清楚
· 在本篇源码解析中,Gson 内部还有一些拓展功能,比如 JsonElement、JsonParser 等的工具类没有提及
· 仅为个人的学习笔记,可能存在错误或者表述不清的地方,有缘补充