Gson 源碼分析 總結

1   Gson 的簡單使用:

簡單對象的序列化和反序列化:
Gson gson = new Gson(); // Or use new GsonBuilder().create();
MyType target = new MyType();
String json = gson.toJson(target); // serializes target to Json
MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
集合類型的序列化和反序列化:
Type listType = new TypeToken<List<String>>() {}.getType();
List<String> target = new LinkedList<String>();
target.add("blah");
Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);


2   Gson 的源碼解析:
 
2.1  Gson 的總體結構


Gson的類結構很簡單,共約12個類,8個接口,2個枚舉。其中主要和外界打交道的類就com.google.gson.Gson和com.google.gson.GsonBuilder這兩個門面類,這兩個類都提供了構造,序列化和反序列化等解析器所需要的幾乎所有功能。
GsonGsonBuilder他們的具體區別如下:
    -  通過Gson()構造的解析器,調用 toJson()方法生成的json字符串中刪除了所有不必要的空白。可以通過設置GsonBuilder.setPrettyPrinting().來改變這規則。
    -  生成的JSON省略了所有爲空的字段。注意在數組作爲null,因爲數組是有序列表。但是,如果一個字段沒有空,但其生成JSON爲空,這個字段是保留的。可以通過設置GsonBuilder.serializeNulls().來使  Gson系列化空的字段。
    -  Gson 提供了默認的序列化和反序列化的基本類型,如Enums, MapURLURILocaleDateBigDecimal,  BigInteger classes等,這些基本上和Java中的類型是一樣的,如果想改變默認的表現形式,可以通過註冊adapterType,設置GsonBuilder.registerTypeAdapter(Type, Object).來完成。
    - Gson 中默認的 Date 的表現格式和Java中是一樣的如DateFormat.DEFAULT.這種格式序列化期間忽略了日期的毫秒部分。可以通過GsonBuilder.setDateFormat(int) or GsonBuilder.setDateFormat(String).來改變。
    - Gson 中默認是忽略@Expose  @ Since 的註解的,可以通過設置GsonBuilder.excludeFieldsWithoutExposeAnnotation().和GsonBuilder.setVersion(double).改變序列化和反序列化規則。
    -  默認的字段命名策略爲:輸出Json和Java中的對象是一樣。可以通過設置 GsonBuilder.setFieldNamingPolicy(FieldNamingPolicy).來自定義這個規則。
    -  默認的情況下,Gson將擦除所有標記爲瞬態或靜態的字段。可以通過GsonBuilder.excludeFieldsWithModifiers(int...).來配置擦除所有類中指定的修飾符的字段。

2.2  Gson 的解析過程:

       Gson解析中序列化比較簡單。這裏主要說下反序列化,主要涉及到Java中的泛型和類型的獲取
       Java泛型的實現機制,使用了泛型的代碼在運行期間相關的泛型參數的類型會被擦除,我們無法在運行期間獲知泛型參數的具體類型(所有的泛型類型在運行時都是Object類型)。但是編譯java源代碼生成的Class文件中還是保存了泛型相關的信息,這些信息被保存在class字節碼常量池中,使用了泛型的代碼處會生成一個signature簽名字段,該簽名signature字段指明瞭這個常量池的地址,於是可從該常量池中獲取到具體的類型。比如Class類的getGenericSuperClass()方法,對於帶有泛型的class,返回一個ParameterizedType對象,對於Object、接口和原始類型返回null,對於數組class則是返回Object.class。ParameterizedType是表示帶有泛型參數的類型的Java類型,JDK1.5引入了泛型之後,Java中所有的Class都實現了Type接口,ParameterizedType則是繼承了Type接口,所有包含泛型的Class類都會實現這個接口。 
        對於簡單的類對象,可以通過傳遞一個Clazz進去,然後通過反射的方法來獲得實際對象如 (T)clazz.newInstance(),Gson中提供的方法也是如此:
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
return Primitives.wrap(classOfT).cast(object);
}
       對於複雜的集合類型中使用了泛型來限制如 List<Object>,在Gson裏面通過TypeToken類更爲通用的方法來有效的解決泛型的問題
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
@SuppressWarnings("unchecked")
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
注意:TypeToken<T>類的構造方法是Protected的,使用者必須通過繼承的方式,或者匿名類的方式來生成一個TypeToken對象。比如:
TypeToken<List<String>> list = new TypeToken<List<String>>() {};//注意:T不能使用帶有通配符的參數如
Class<?>,或
List<? extends CharSequence>
接下來,執行的是下面這個函數:
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
由於外部不得不採用匿名類的方式來生成TypeToken<?>,因此,需要獲得的泛型類型包含在superclass中。假如你的匿名類不包含任何的泛式類型,就會拋出一個"Miss type paramter"異常。然後,執行getGenericSuperclass()操作,得到的是一個ParameterizedType 類型的Type。而這個Type包含幾乎關心的所有類型元數據,包括它的擁有者,類名和泛型參數等。最後,根據靜態的canonicalize 方法,規範在泛型內的參數,並且轉化成Gson自己給定的數據結構,返回獲取到的Type信息。具體如何獲取類型的代碼,可以看看Gson源碼。


3   Gson 的特殊功能的使用:

3.1 多個類的嵌套的例子

GSON可以很容易地序列化和反序列化靜態嵌套類。
但是GSON不能自動反序列化的純內部類,因爲它們的無參構造函數需要引用的對象在反序列化時是無法使用的.

你可以通過使用靜態內部類或給它提供一個定製的InstanceCreator來解決這個問題。下面是一個例子:

//NOTE: 這個class B 在默認情況下不會被`GSON`序列化
public class A {
public String a;
class B {
public String b;
publicB() {// No args constructor for B
}
}
}
}

GSON不能反序列化{"b":"abc"} ,因爲class B是一個內部類,如果你這麼定義B: static class B, GSON是能反序列化這段字符串的, 另外一個辦法就是自定義個實例構建方法,下面是一個例子

//NOTE: 這樣可行,但是不推薦
public class InstanceCreatorForB implements InstanceCreator<A.B> {
privatefinal A a;
public InstanceCreatorForB(A a){
this.a= a;
}
public A.BcreateInstance(Type type){
return a.newB();
}
}

3.2 集合的例子

Gson gson =new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);
//(序列化Serialization)
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
// (反序列化Deserialization)
Type collectionType =new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);//ints,ints2是一樣的

集合限制

  • 能夠序列化任何任意類型的集合,但是不能讓它反序列化,因爲使用者沒有辦法給指示數據類型
  • 反序列化的時候,集合一定得是一個特定泛型的集合

3.3 泛型的序列化和反序列化

當你調用toJson(obj)的時候,GSON會執行obj.getClass()來獲取序列化的字段的信息,同樣的,你可以在fromJson(json, MyClass.class)方法中使用典型對象.如果對象是一個非泛型對象,這樣也能正常工作.但是, 如果對象是一個泛型對象,Java的類型擦除會讓這個對象丟失泛型類型信息.

可以通過使用TypeToken指定正確的參數化類型的泛型類型來解決這個問題。原理就是上面2.2 Gson解析部分。


3.4 任意對象集合的序列化和反序列化

有時你會處理一些混合類型的JSON,比如

['hello',5,{name:'GREETINGS',source:'guest'}]
static class Event{
private String name;
private String source;
privateEvent(String name, String source){
this.name= name;
this.source= source;
}
@Override
public String toString(){
return String.format("(name=%s, source=%s)", name, source);
}
}

public static void main(String[] args){
Gson gson =new Gson();
Collection collection =new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS","guest"));
String json = gson.toJson(collection);
System.out.println("Using Gson.toJson() on a raw collection: "+ json);
JsonParser parser =new JsonParser();
JsonArray array = parser.parse(json).getAsJsonArray();
String message = gson.fromJson(array.get(0), String.class);
int number = gson.fromJson(array.get(1),int.class);
Event event = gson.fromJson(array.get(2), Event.class);
System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event.toString());
}

使用GSON序列化這個集合只需要調用toJson(collection),而且不用設置其他任何東西.但是你要是通過fromJson(json, Collection.class)反序列化這個集合的話是不可行的,因爲GSON,沒辦法匹配集合類型,所以GSON需要你提供這個集合序列化的類型.有三個選則:

  • 使用GSON的解析器API(底層流解析器或DOM解析器JsonParser)來解析數據元素,然後在每一個元素上使用Gson.fromJson().這是首選的方法。
  • Collection.class註冊類型適配器,讓每一個元素都對應自己的對象.缺點是會搞亂GSON`中其他的集合的反序列化.
  • 通過註冊一個MyCollectionMemberType使用fromJsonCollection<MyCollectionMemberType>,缺點就是隻有數組是頂級元素纔是可行的

3.5 從序列化和反序列化中剔除字段

GSON支持剔除頂層類,字段和字段類型,下面是一種排除字段或者類的可插拔的機制,如果這些機制沒有滿足你的需要.你可以自定義序列化和反序列化的解釋器.

默認的情況下,如果一個對象被聲明爲transient,static,那麼它就會被剔除.  如果你要包含這些字段,可以用如下代碼:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC)
.create();
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
.create();

自定義的剔除策略

如果這些默認的策略都不能滿足你的需求,你還可以自定自己的策略,更多可見ExclusionStrategy
下面是一個使用@Foo的例子

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Foo {
// Field tag only annotation
}
public class SampleObjectForTest {
@Foo private final int annotatedField;
private final String stringField;
private final long longField;
private final Class<?> clazzField;

public SampleObjectForTest() {
annotatedField = 5;
stringField = "someDefaultValue";
longField = 1234;
}
}

public class MyExclusionStrategy implements ExclusionStrategy {
private final Class<?> typeToSkip;

private MyExclusionStrategy(Class<?> typeToSkip) {
this.typeToSkip = typeToSkip;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return (clazz == typeToSkip);
}
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(Foo.class) != null;
}
}

public static void main(String[] args) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new MyExclusionStrategy(String.class))
.serializeNulls()
.create();
SampleObjectForTest src = new SampleObjectForTest();
String json = gson.toJson(src);
System.out.println(json);
}
======== OUTPUT ========
{"longField":1234}

3.6 自定義序列化和反序列化

有時默認的內置序列化或反序列化的描述,並不是你想要的,比如做時間之類的處理的時候這種問題經常出現,Gson允許你自定義序列化工具:

  • Json Serialiers: 需要定義自定義序列化對象
  • Json Deserializers: 需要定義自定義反序列化的類型
  • Instance Creators: 無參構造方法和反序列化解析器都不是必須的
3.7  其他 

  • 緊湊漂亮的JSON輸出
  • NULL對象的支持
  • 版本支持
  • 字段命名支持

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章