Gson 源碼解讀

轉載請註明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_

開源庫地址:https://github.com/google/gson
解讀版本:2.7

Gson是一個可以用來將Java對象轉換爲JSON字符串的Java庫。當然,它也可以把JSON字符串轉換爲等價的Java對象。網上已經有了不少可將Java對象轉換成JSON的開源項目。但是,大多數都要求你在Java類中加入註解,如果你無法修改源碼的話就比較坑爹了,此外大多數開源庫並沒有對泛型提供完全的支持。於是,Gson在這兩個重要的設計目標下誕生了。Gson可以作用於任意的Java對象(包括接觸不到源碼的),與此同時還加入了完整的泛型支持。

基本用法

  • 基本數據類型
// 序列化爲json
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// 反序列化爲java object
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
  • 對象
public class User {
    public String name="maplejaw";
    public int age=18;

    public User(){
     //無參構造方法
    }
}


// 序列化
 User obj = new User();
 Gson gson = new Gson();
 String json = gson.toJson(obj);  // ==> json is {"name":"maplejaw","age":18}

//反序列化
 User obj2 = gson.fromJson(json, User.class);
  • 泛型
Type type= new TypeToken<List<User>>(){}.getType();
List<User> list = gson.fromJson(json, type);
  • 配置Gson
  Gson gson=new GsonBuilder()
                 .setLenient() //設置寬鬆的容錯性
                 .setPrettyPrinting() //設置漂亮的打印(打印出來的有縮進風格)
                 .setVersion(..) //設置當前版本號
                 ...
                 .create();

Gson的基本用法如上所示,使用起來比較簡便,一般情況下也已經夠用了。需要注意的是,Java對象一定要有一個無參構造方法,這是Gson實例化對象的關鍵(自定義實例後面介紹)。此外,Java對象中並不要求有set/get方法。一般只要瞭解以上基本用法就能夠應付絕大多數情況了,如果想深入瞭解一些高級用法,請繼續往下閱讀源碼解讀。

源碼解讀

Gson中的5個註解

  • @Expose
    表示某個成員變量暴露於JSON序列化/反序列化。只需在GsonBuilder中配置excludeFieldsWithoutExposeAnnotation()時纔會生效。當配置過後,只有使用了@Expose註解的成員變量纔會參與序列化/反序列化工作。

    public class User {
    @Expose
    private String firstName;//參與序列化(JAVA對象轉爲JSON)/反序列化(JSON轉爲JAVA對象)
    
    @Expose(serialize = false) 
    private String lastName;//參與反序列化
    
    @Expose (serialize = false, deserialize = false) 
    private String emailAddress; //不參與
    
    private String password;//不參與
    }
  • @SerializedName
    表示某個成員變量序列化/反序列化的名字,無需在GsonBuilder中配置就能生效,甚至會覆蓋掉FieldNamingPolicy

    public class MyClass {
    @SerializedName("name") //a => name
    String a;
    @SerializedName(value="name1", alternate={"name2", "name3"}) 
    String b;
    
    String c;
    
    public MyClass(String a, String b, String c) {
     this.a = a;
     this.b = b;
     this.c = c;
    }
    }
    

    當使用了該註解後,此時序列化/反序列化的情況如下:

    MyClass target = new MyClass("v1", "v2", "v3");
    Gson gson = new Gson();
    String json = gson.toJson(target);
    //===== 輸入的json如下 =====
    {"name":"v1","name1":"v2","c":"v3"}
    //同理,以下的json數據都能成功反序列化爲MyClass。
    {"name":"v1","name1":"v2","c":"v3"}
    {"name":"v1","name2":"v2","c":"v3"}
    {"name":"v1","name3":"v2","c":"v3"}

    這個註解有什麼好處呢?衆所周知,在Java中的變量命名規則須遵守駝峯原則,但是,如果後臺使用的是php等其他語言,將導致命名規則不一致;又或者因爲其他原因導致多條json只有一個字段不一致,總不能建立多個實體類吧?這時候這個註解就派上用場了。

  • @Since
    表示某個成員變量從哪個版本開始生效,只在GsonBuilder中配置了setVersion()時纔會生效。

    public class User {
    private String firstName;//一直參與
    private String lastName;//一直參與
    @Since(1.0) private String emailAddress; //當前版本>=1.0時纔會參與序列化/反序列化,否則忽略
    @Since(1.0) private String password;//當前版本>=1.0時纔會參與序列化/反序列化,否則忽略
    @Since(1.1) private Address address;//當前版本>=1.1時纔會參與序列化/反序列化,否則忽略
    }
  • @Until
    表示某個成員變量從哪個版本開始失效,只在GsonBuilder中配置了setVersion()時纔會生效。

     public class User {
      private String firstName;//一直參與
      private String lastName;//一直參與
      @Until(1.1) private String emailAddress;//當前版本<=1.1時參加序列化/反序列化
      @Until(1.1) private String password;//當前版本<=1.1時參加序列化/反序列化
     }
  • @JsonAdapter
    表示在某一個成員變量或者類上使用TypeAdapter。至於TypeAdapter是什麼東東,後面介紹。舉個例子:
    假如有個User類如此下:

     public class User {
      public final String firstName, lastName;
      private User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
     }
    }

    準備編寫一個UserJsonAdapter。

    public class UserJsonAdapter extends TypeAdapter<User> {
    @Override 
    public void write(JsonWriter out, User user) throws IOException {
     out.beginObject();
     out.name("name");
     out.value(user.firstName + " " + user.lastName);
     out.endObject();
    }
    @Override 
    public User read(JsonReader in) throws IOException {
     in.beginObject();
     in.nextName();
     String[] nameParts = in.nextString().split(" ");
     in.endObject();
     return new User(nameParts[0], nameParts[1]);
    }
    }
    

    將UserJsonAdapter應用到屬性:此時,Gadget中的User,將會按照UserJsonAdapter來進行序列化/反序列化

    private static final class Gadget {
    @JsonAdapter(UserJsonAdapter.class)
    public User user;
    }
    

    將UserJsonAdapter應用到類:此時,在序列化/反序列User對象時,都會按照UserJsonAdapter來執行。

     @JsonAdapter(UserJsonAdapter.class)
     public class User {
      public final String firstName, lastName;
      private User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
     }
    }

JsonReader/JsonWriter

在Gson中,Java對象與JSON字符串之間的轉換是通過字符流來進行操作的。JsonReader繼承於Reader用來讀取字符,JsonWriter繼承於Writer用來寫入字符。
假如,現在有如下一段JSON數據用來表示一個用戶列表。name爲名字,age爲年齡,geo爲定位的經緯度。如何使用JsonReader轉爲java對象?
image_1amg2niju16qh14gc1h3odm6k7q13.png-11.3kB
首先查看一下json的結構,不難發現最外層是一個數組。於是,我們定義出如下readJsonStream從Stream(流)中讀取用戶列表,讀取完畢後,務必記得關閉流。


//從流中讀取List<User>
public List<User> readJsonStream(InputStream in) throws IOException {
     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
     try {
       return readUserArray(reader);//讀取用戶列表
     } finally {
       reader.close();//關閉流
     }
   }

//讀取List<User>
   public List<User> readUserArray(JsonReader reader) throws IOException {
     List<User> users = new ArrayList<User>();
     reader.beginArray();//開始讀取數組
     while (reader.hasNext()) {//是否還有下個元素
       users.add(readUser(reader));//讀取下個元素
     }
     reader.endArray();//結束讀取數組
     return users;
   }

 //讀取User對象  
public User readUser(JsonReader reader) throws IOException {
       String name = null;
       int age = -1;
       List<Double> geo=null;
      reader.beginObject();//開始讀取對象
     while (reader.hasNext()) {//是否還有下個元素
       String name = reader.nextName();//讀取下一個json屬性名
        //判斷屬性名是哪一個
       if (name.equals("name")) {
           name = reader.nextString();
       } else if (name.equals("age")) {
           age = reader.nextInt();
       } else if (name.equals("geo")&& reader.peek() != JsonToken.NULL) {
          geo = readDoublesArray(reader);
       } else {
         reader.skipValue();//忽略沒有匹配到內容的值
       }
     }
     reader.endObject();//結束讀取對象
     return new User(name, age,geo);
   }
   //讀取經緯度
   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
     List<Double> doubles = new ArrayList<Double>();

     reader.beginArray();
     while (reader.hasNext()) {
       doubles.add(reader.nextDouble());
     }
     reader.endArray();
     return doubles;
   }

我們不難發現如下規律,每次讀取數組或者對象之前,都必須調用beginObject()或者beginArray(),讀完相應內容後,也必須調用endObject()或者endArray()reader.hasNext()用來判斷是否還有下一個元素,然後調用nextName來取下一個屬性名,以及一系列的nextXXX來取相應的值。
上面介紹的是將json字符串轉換爲java對象的用法,現在來看看如何使用JsonWriter來講java對象轉爲json字符串。

   //將List<User>寫入流中
   public void writeJsonStream(OutputStream out, List<User> users) throws IOException {
     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
     writer.setIndent("    ");//設置縮進風格(設置後寫入的字符串保持縮進風格,如果沒有設置,將會頂格打印)
     writeUsersArray(writer, users);//寫入List<User>
     writer.close();
   }

  //寫入List<User>
   public void writeUsersArray(JsonWriter writer, List<User> users) throws IOException {
     writer.beginArray();//開始寫入數組
     for (User user : users) {
       writeUser(writer, user);
     }
     writer.endArray();//結束寫入數組
   }
   //寫入User
   public void writeUser(JsonWriter writer, User user) throws IOException {
     writer.beginObject();//開始寫入對象
     writer.name("name").value(user.getName());
     writer.name("age").value(user.getAge());
     if (user.getGeo() != null) {
       writer.name("geo");
       writeDoublesArray(writer, user.getGeo());
     } else {
       writer.name("geo").nullValue();
     }
     writer.endObject();//結束寫入對象
   }

   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
     writer.beginArray();
     for (Double value : doubles) {
       writer.value(value);
     }
     writer.endArray();
   }

JsonWriter用來寫入JSON字符串的規則和JsonWriter大相徑庭,每次寫入數組或者對象之前,都必須調用beginObject()或者beginArray(),寫完相應內容後,也必須調用endObject()或者endArray()name(xx)用來寫入名字,value(XX)用來寫入值。
接下來,深入淺出解析源碼!stream包的結構如下:
image_1amldac2a1qe33br1n9chm1n4s9.png-8.6kB
枚舉類JsonToken的源碼如下,主要用於表示JSON字符串中的名字/值的結構。

public enum JsonToken {

  BEGIN_ARRAY,//JSON array開始

  END_ARRAY,//JSON array結束

  BEGIN_OBJECT,//JSON object開始

  END_OBJECT,//JSON object結束

  NAME,//JSON 屬性名,JsonReader#nextName/JsonWriter#name

  STRING,//JSON 字符串

  NUMBER,//JSON 數字,代表java中double,long,int

  BOOLEAN,//JSON 布爾值

  NULL, //表示 null

  END_DOCUMENT //表示JSON流的結尾
}

JsonScope是一個常量類,元素詞法作用域。用來標識JsonReader/JsonWriter現在讀/寫到哪了。

final class JsonScope {

    static final int EMPTY_ARRAY = 1;//沒有元素的數組(相當於之前剛讀了“[”),下一個元素一定不是逗號。


    static final int NONEMPTY_ARRAY = 2;//非空數組(至少已經有一個元素),下一個元素不是逗號就是“]”


    static final int EMPTY_OBJECT = 3;//空對象(剛讀到“{”,一個name/value對都沒有),下一個一定不是逗號。


    static final int DANGLING_NAME = 4;//名字,下一個元素一定是值。


    static final int NONEMPTY_OBJECT = 5;//非空對象(至少一個name/value對),下一個元素不是逗號就是“}”


    static final int EMPTY_DOCUMENT = 6;//空文檔,初識狀態,啥也沒讀


    static final int NONEMPTY_DOCUMENT = 7;//文檔中有一個頂級的數組/對象


    static final int CLOSED = 8;//文檔已被關閉
}

JsonWriter

JsonWriter,用於將Java對象寫爲JSON字符串。使用前我們可以進行一些默認的配置:

  //設置縮進符號,只要你設置了縮進符號,打印出來的字符串將會擁有縮進風格,非常漂亮
  public final void setIndent(String indent)
  //設置寬鬆的容錯性(頂級值可以不是爲object/array,數字可以爲無窮)
  public final void setLenient(boolean lenient)
  //html轉義
  public final void setHtmlSafe(boolean htmlSafe)
  //序列化空,默認true
  public final void setSerializeNulls(boolean serializeNulls) 

JsonWriter使用一個數組來保存當前的寫入狀態(就是標識寫到哪了),JsonScope中已經介紹過了。

  private int[] stack = new int[32];
  private int stackSize = 0;
  {
    push(EMPTY_DOCUMENT);
  }

注意stack並沒有定死32的長度,當寫滿時將會擴大一倍。使用push來保存當前寫入狀態,peek查看當前狀態。

  //將當前狀態保存棧頂
  private void push(int newTop) {
    if (stackSize == stack.length) {//如果滿了就擴大一倍
      int[] newStack = new int[stackSize * 2];
      System.arraycopy(stack, 0, newStack, 0, stackSize);
      stack = newStack;
    }
    stack[stackSize++] = newTop;
  }

  //查看棧頂的值
  private int peek() {
    if (stackSize == 0) {
      throw new IllegalStateException("JsonWriter is closed.");
    }
    return stack[stackSize - 1];
  }

在寫一個對象之前,必須使用beginObject()。一定很好奇beginObject()做了什麼,當時還不忙着看,先來看看怎麼寫入name/value的。
name(XX)源碼如下:

  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.");
    }
    deferredName = name;//賦值給deferredName
    return this;
  }

看完源碼不由覺得有點失望,只是賦值給了deferredName,其他什麼事也沒做。那麼到底什麼時候寫入name的。這還得從寫入value看起。
value(XX)有很多重載函數。
image_1amlgoj7n7bv1s5u13f8cv2baqm.png-9.9kB
我們只看value(String)這種。

  public JsonWriter value(String value) throws IOException {
    if (value == null) {//如果空的話,寫入空值
      return nullValue();
    }
    writeDeferredName();//寫入name。
    beforeValue();//寫入“:”(會對上次狀態進行校驗)
    string(value);//寫入value(會對特殊字符進行轉碼)
    return this;
  }

通過writeDeferredName寫入名字,beforeValue寫入:,string寫入真正的值。當然writeDeferredName也不是說直接寫入name,而是先beforeName進行狀態校驗,換行,替換狀態,按需寫入,,然後使用string寫入name值。

  private void writeDeferredName() throws IOException {
    if (deferredName != null) {
      beforeName();//校驗等
      string(deferredName);//寫入名字
      deferredName = null;
    }
  }

現在來看看beginObject,如果該對象是name/value中的value,那麼writeDeferredName用來寫入name。open寫入{以及更改棧頂狀態。

    public JsonWriter beginObject() throws IOException {
    writeDeferredName();//寫入name(如果有)
    return open(EMPTY_OBJECT, "{"); //寫入“{”
  }

endObject則是調用close來寫入},傳入的EMPTY_OBJECT,EMPTY_OBJECT用來比較是在哪種狀態下進行關閉的。

  public JsonWriter endObject() throws IOException {
    return close(EMPTY_OBJECT, EMPTY_OBJECT, "}");
  }

至於beginArray/endArray的源碼這裏就不贅述了。

JsonReader

JsonReader,用於將JSON字符串轉爲Java對象。
JsonReader中的setLenient的容錯性那可是非常厲害。以下錯誤都能被忽略。

  • )]}'\n 前綴
  • 多個頂級值
  • 頂級值不是 object/array類型
  • 數字類型爲無窮數,或者不是個數字
  • 一行的結尾存在//或者 #註釋
  • C語言風格的註釋//
  • name用了單引號或者沒用引號
  • string用了單引號或者沒用引號
  • 數組元素的分隔符用了;而不是,
  • name和value不是用:分隔,而是用=或=>
  • name/value對之間不是逗號分隔,而是;分隔

和JsonWriter一樣,也使用一個數組stack來保存當前的讀取到的字符的類型和狀態。其中引入了一系列的PEEKED_XXint常量來記錄讀取的字符類型。由於篇幅問題,JsonReader就不深入研究了,以下列出JsonReader中的核心方法。

  • fillBuffer 用來讀取字符到buffer數組中
  • nextNonWhitespace 用來讀取非空格/註釋/換行等字符
  • peek() 查看元素類型,對應於JsonToken中的值。
  • doPeek() 內部使用了nextNonWhitespace讀取一個字符,然後設置當前的讀取的字符類型。
  • hasNext object/array中是不是還有下一個元素
  • nextName讀取下一個name
  • nextString/nextInt/nextBoolean/nextLong/nextDouble/nextNull用來讀取下一個對應的值。
  • skipValue 跳過下一個value
  • nextQuotedValue/nextUnquotedValue用來讀取下一個單引號/雙引號沒有引號括起來的name或者string value
  • beginObject/endObject/beginArray/endArray 用來消費下一個對應類型的字符

Gson中的泛型

在瞭解Gson中的泛型前,我們來看兩個類,$Gson$Types和$Gson$Preconditions。
$Gson$Types,專門用來處理泛型類型。核心源碼如下:

  //規範化類型
  public static Type canonicalize(Type type) {
    if (type instanceof Class) {//class
      Class<?> c = (Class<?>) type;
      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;

    } else if (type instanceof ParameterizedType) {//泛型
      ParameterizedType p = (ParameterizedType) type;
      return new ParameterizedTypeImpl(p.getOwnerType(),
          p.getRawType(), p.getActualTypeArguments());

    } else if (type instanceof GenericArrayType) {//數組類型
      GenericArrayType g = (GenericArrayType) type;
      return new GenericArrayTypeImpl(g.getGenericComponentType());

    } else if (type instanceof WildcardType) {//通配符類型
      WildcardType w = (WildcardType) type;
      return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());

    } else {
      // type is either serializable as-is or unsupported
      return type;
    }
  }




  //獲取原始類型
    public static Class<?> getRawType(Type type) {
    if (type instanceof Class<?>) {

      return (Class<?>) type;

    } else if (type instanceof ParameterizedType) {
      //泛型
      ParameterizedType parameterizedType = (ParameterizedType) type;

      Type rawType = parameterizedType.getRawType();
      checkArgument(rawType instanceof Class);
      return (Class<?>) rawType;

    } else if (type instanceof GenericArrayType) {
      //數組類型
      Type componentType = ((GenericArrayType)type).getGenericComponentType();
      return Array.newInstance(getRawType(componentType), 0).getClass();

    } else if (type instanceof TypeVariable) {
           //類型變量的上邊界是Object
           return Object.class;

    } else if (type instanceof WildcardType) {
       //返回上邊界
      return getRawType(((WildcardType) type).getUpperBounds()[0]);

    } else {
      String className = type == null ? "null" : type.getClass().getName();
      throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
          + "GenericArrayType, but <" + type + "> is of type " + className);
    }
  }

canonicalize這個方法中返回了一系列的XXImpl,其實只是實現了java.io.Serializable接口,重寫了equal方法而已。其中ParameterizedType/GenericArrayType/WildcardType,熟悉泛型的應該不會陌生,這裏就不贅述了。

$Gson$Preconditions,這個類用於條件校驗,源碼很是簡潔。

public final class $Gson$Preconditions {
  private $Gson$Preconditions() {
    throw new UnsupportedOperationException();
  }

  public static <T> T checkNotNull(T obj) {//校驗非空
    if (obj == null) {
      throw new NullPointerException();
    }
    return obj;
  }

  public static void checkArgument(boolean condition) {//校驗是不是滿足條件
    if (!condition) {
      throw new IllegalArgumentException();
    }
  }
}

現在回過來瞭解Gson中的泛型,Gson中用TypeToken來表示泛型。
源碼如下:

public class TypeToken<T> {
  final Class<? super T> rawType;//T的原始類型
  final Type type; //T 的類型
  final int hashCode;

  protected TypeToken() {
   //獲取父類泛型的參數類型
    this.type = getSuperclassTypeParameter(getClass());
    //獲取原始類型
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
    this.hashCode = type.hashCode();
  }

   public final Class<? super T> getRawType() {
    return rawType;
  }

  public final Type getType() {
    return type;
  }

  //..
  //省略了部分源碼

  }

由於Java中只能通過getGenericSuperclass獲取父類泛型類型,所以TypeToken必須new出一個子類Type type= new TypeToken<List<User>>(){}.getType();來使用。
getSuperclassTypeParameter的源碼如下:

  static Type getSuperclassTypeParameter(Class<?> subclass) {
    Type superclass = subclass.getGenericSuperclass();//獲取父類泛型類型
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    ParameterizedType parameterized = (ParameterizedType) superclass;
    //獲取泛型參數類型,即T的類型
    return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
  }

JsonElement

JsonElement,一個抽象類,代表着JSON中的元素類型。可以表示JsonObject,JsonArray,JsonPrimitive,JsonNull。換言之,Gson中的JsonObject/JsonArray/JsonPrimitive/JsonNull繼承於JsonElement。JsonElement也提供了一系列的getAsXXX方法來獲取元素。
我們着重看一下JsonPrimitive,JsonPrimitive代表着java中的基本數據類型。可以看出,通過構造方法進行賦值,然後通過getAsXX取值。

public final class JsonPrimitive extends JsonElement {

//基本類型列表如下
  private static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
      float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
      Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };

  //值
  private Object value;

    public JsonPrimitive(Boolean bool) {
    setValue(bool);
  }

  //賦值數字類型
  public JsonPrimitive(Number number) {
    setValue(number);
  }

 //賦值String
  public JsonPrimitive(String string) {
    setValue(string);
  }

  //賦值字符
  public JsonPrimitive(Character c) {
    setValue(c);
  }

  //賦值給value
void setValue(Object primitive) {
    if (primitive instanceof Character) {

      char c = ((Character) primitive).charValue();
      this.value = String.valueOf(c);
    } else {
      $Gson$Preconditions.checkArgument(primitive instanceof Number
              || isPrimitiveOrString(primitive));
      this.value = primitive;
    }
  }
  //..
  //省略了部分源碼
  }

Gson中的TypeAdapter

還記得介紹註解時提到的TypeAdapter嗎?TypeAdapter是一個抽象類,可以用來自定義類型轉換。源碼如下。

public abstract class TypeAdapter<T> {

  //抽象方法,寫value(an array, object, string, number, boolean or null)
  public abstract void write(JsonWriter out, T value) throws IOException;

 //抽象方法,讀value
  public abstract T read(JsonReader in) throws IOException;

  //包裝方法,幫你處理了空值,你可以不用擔心空值問題
  public final TypeAdapter<T> nullSafe() {
    return new TypeAdapter<T>() {
      @Override public void write(JsonWriter out, T value) throws IOException {
        if (value == null) {//如果爲空,寫入null
          out.nullValue();
        } else {
          TypeAdapter.this.write(out, value);
        }
      }
      @Override public T read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
          reader.nextNull();
          return null;
        }
        return TypeAdapter.this.read(reader);
      }
    };
  }




  //..
  //省略了部分源碼

}

我們只需繼承於TypeAdapter,實現相應方法就能實現自己的TypeAdapter,前面我們介紹了使用註解方法來使用TypeAdapter,現在來講一下在GsonBuilder中如何使用。

  Gson gson= new GsonBuilder()
                .registerTypeAdapter(XX.class,new XXTypeAdapter())
                .create();

在GsonBuilder中使用registerTypeAdapter配置後,就不需要使用相關注解了。那麼問題來了,如果GsonBuilder和註解爲同一個類配置了不同的TypeAdapter會發生什麼狀況?我可以很負責任的告訴你,註解的優先級是最高的。
此外,我們之前在編寫UserJsonAdapter時沒有處理空值情況,很容易會拋出異常,那怎麼辦?一種是自己處理空值情況,將代碼改成如下形式。

public class UserJsonAdapter extends TypeAdapter<User> {
@Override 
public void write(JsonWriter out, User user) throws IOException {
if (user == null) {//判斷空值
    out.nullValue();
    retrun;
  }
 out.beginObject();
 out.name("name");
 out.value(user.firstName + " " + user.lastName);
 out.endObject();
}
@Override 
public User read(JsonReader in) throws IOException {
 if (reader.peek() == JsonToken.NULL) {//判斷空值
         reader.nextNull();
         return null;
    }
 in.beginObject();
 in.nextName();
 String[] nameParts = in.nextString().split(" ");
 in.endObject();
 return new User(nameParts[0], nameParts[1]);
}
}

第二種方法,使用nullSafe:

 Gson gson= new GsonBuilder()
                .registerTypeAdapter(User.class,new UserJsonAdapter().nullSafe())
                .create();

TypeAdapterFactory是一個創造TypeAdapter的工廠,用來創造一些相似類型的TypeAdapter。

public interface TypeAdapterFactory {

  <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}

繼承TypeAdapter,需要重寫writeread相關方法,可是隻想處理序列化和反序列化中的一種該怎麼辦?那麼接下來就該介紹JsonSerializerJsonDeserializer接口了。可以在@JsonAdapter註解和registerTypeAdapter中註冊使用。


//序列化
public interface JsonSerializer<T> {

  public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}

//反序列化
public interface JsonDeserializer<T> {

  public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)throws JsonParseException;
}

序列化策略

  • LongSerializationPolicy
    枚舉類,指定長整型的序列化類型,默認有DEFAULT,STRING兩種類型,可繼承它實現其他類型

  • InstanceCreator 實例創造器
    當反序列化時需要實例化對象,但是假如該對象沒有默認構造方法怎麼吧?那麼就自定義自己的實例創造器。InstanceCreator一般配合ConstructorConstructor一起使用。

    public interface InstanceCreator<T> {
    
    public T createInstance(Type type);
    }
    class UserInstanceCreator implements InstanceCreator<User> {
    public User createInstance(Type type) {
      return new User(null, -1);
    }
    }
  • FieldNamingStrategy
    提供了一個自定義的字段命名機制

    public interface FieldNamingStrategy {
    public String translateName(Field f);
    }
    
  • FieldNamingPolicy
    一個枚舉類,實現了FieldNamingStrategy,提供了一些默認的字段命名機制,IDENTITY:原始機制;UPPER_CAMEL_CASE:首字母大寫的駝峯映射;UPPER_CAMEL_CASE_WITH_SPACES:用空格分隔的大寫駝峯LOWER_CASE_WITH_UNDERSCORES:下劃線相連的小寫映射;LOWER_CASE_WITH_DASHES:虛線相連的小寫映射。

  • FieldAttributes
    用來存取字段的屬性:getName獲取字段名,getDeclaringClass獲取聲明的類,getDeclaredType獲取字段的聲明類型,getAnnotation獲取註解。

  • ExclusionStrategy
    一個用於定義排除策略的的接口。

    public interface ExclusionStrategy {
    //是否應該忽略該屬性
    public boolean shouldSkipField(FieldAttributes f);
    //是否應該忽略該類
    public boolean shouldSkipClass(Class<?> clazz);
    }
    

其他

  • Excluder 排除器,主要用於根據策略和註解來判斷哪些字段應該被忽略。
    屬性如下:

    private static final double IGNORE_VERSIONS = -1.0d;
    public static final Excluder DEFAULT = new Excluder();
    //默認忽略版本號
    private double version = IGNORE_VERSIONS;
    //默認以下修飾符的字段會被忽略
    private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
    private boolean serializeInnerClasses = true;//序列化內部類
    private boolean requireExpose;//需要Expose註解?
    //存放序列化排除策略
    private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
    //存放反序列化排除策略
    private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
    

    所有的方法如下:
    image_1amm2a5vkurd1t6ku4412do8om9.png-45.5kB

  • Primitives 一個工具類,用於在原始類型和包裝類型間轉化。wrap包裝,unwrap解開。

  • ObjectConstructor 一個人通用的構造器接口。

    public interface ObjectConstructor<T> {
      public T construct();
    }
  • ConstructorConstructor 保存實例構造器集合的類

    public final class ConstructorConstructor {
     //實例創造器集合
     private final Map<Type, InstanceCreator<?>> instanceCreators;
     //通過類型返回一個構造器
    
    public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
    
    final Type type = typeToken.getType();//獲取類型
    final Class<? super T> rawType = typeToken.getRawType();//獲取真實類型
    
    //首先從集合中獲取看有沒有相同的Type,有就創造實例並返回
    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
    if (typeCreator != null) {
      return new ObjectConstructor<T>() {
        @Override public T construct() {
          return typeCreator.createInstance(type);
        }
      };
    }
    
    
    //然後根據原始類型取,去集合中取
    final InstanceCreator<T> rawTypeCreator =
        (InstanceCreator<T>) instanceCreators.get(rawType);
    if (rawTypeCreator != null) {
      return new ObjectConstructor<T>() {
        @Override public T construct() {
          return rawTypeCreator.createInstance(type);
        }
      };
    }
    
    //獲取默認構造方法
    ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
    if (defaultConstructor != null) {
      return defaultConstructor;
    }
    
    //獲取集合類型的構造器(Collection,EnumSet,Set,Queue,Map)
    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
    if (defaultImplementation != null) {
      return defaultImplementation;
    }
    
    // 使用不安全的分配器
    return newUnsafeAllocator(type, rawType);
    }
     //..
     //省略了部分源碼
     }
  • JsonStreamParser 解析器,解析爲JsonElement。hasNext:是否有下一個元素,next取下一個元素,返回JsonElement。

  • Streams 內部使用TypeAdapters.JSON_ELEMENT寫入/讀取下一個JsonElement。

GSON 源碼解讀

構造方法

現在我們從Gson的構造方法入手,解讀Gson是如何工作的。

 Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
      final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
      boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
      boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
      LongSerializationPolicy longSerializationPolicy,
      List<TypeAdapterFactory> typeAdapterFactories) {



    this.constructorConstructor = new ConstructorConstructor(instanceCreators);//實例構造器
    this.excluder = excluder;//排除器
    this.fieldNamingStrategy = fieldNamingStrategy;//字段命名策略
    this.serializeNulls = serializeNulls;//序列化空
    this.generateNonExecutableJson = generateNonExecutableGson;//生成不可執行前綴(用來防止攻擊)
    this.htmlSafe = htmlSafe;//html轉義
    this.prettyPrinting = prettyPrinting;//縮進打印
    this.lenient = lenient;//寬鬆的容錯性

    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();//TypeAdapter的工廠列表

    //以下都是往工廠列表加入TypeAdapterFactory

    // 構建不能被重載的TypeAdapter
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);

    // 排除器必須在所有的用於自定義的typeAdapter之前
    factories.add(excluder);

    //用戶自定義的typeAdapter工廠
    factories.addAll(typeAdapterFactories);


    //以下爲默認的TypeAdapter
    factories.add(TypeAdapters.STRING_FACTORY);
    factories.add(TypeAdapters.INTEGER_FACTORY);
    factories.add(TypeAdapters.BOOLEAN_FACTORY);
    factories.add(TypeAdapters.BYTE_FACTORY);
    factories.add(TypeAdapters.SHORT_FACTORY);
    TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
    factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
    factories.add(TypeAdapters.newFactory(double.class, Double.class,
            doubleAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.newFactory(float.class, Float.class,
            floatAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.NUMBER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
    factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
    factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
    factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
    factories.add(TypeAdapters.CHARACTER_FACTORY);
    factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
    factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
    factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
    factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
    factories.add(TypeAdapters.URL_FACTORY);
    factories.add(TypeAdapters.URI_FACTORY);
    factories.add(TypeAdapters.UUID_FACTORY);
    factories.add(TypeAdapters.CURRENCY_FACTORY);
    factories.add(TypeAdapters.LOCALE_FACTORY);
    factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
    factories.add(TypeAdapters.BIT_SET_FACTORY);
    factories.add(DateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CALENDAR_FACTORY);
    factories.add(TimeTypeAdapter.FACTORY);
    factories.add(SqlDateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.TIMESTAMP_FACTORY);
    factories.add(ArrayTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CLASS_FACTORY);


    //複合的TypeAdapter
    factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
    this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
    factories.add(jsonAdapterFactory);
    factories.add(TypeAdapters.ENUM_FACTORY);
    factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));

    this.factories = Collections.unmodifiableList(factories);
  }

構造方法讓人看的眼花繚亂,尤其是往集合中加入了那麼多的TypeAdapterFactory。由於是在太多無法逐一介紹。就挑個最簡單的來講解。總之明白TypeAdapter的作用即可。
ObjectTypeAdapter:內部有一個靜態工廠類,所以只需ObjectTypeAdapter.FACTORY這樣調用即可。

public final class ObjectTypeAdapter extends TypeAdapter<Object> {
  //工廠類
  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    //該工廠用來創建Object類型的TypeAdapter
    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      if (type.getRawType() == Object.class) {
        return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
      }
      return null;
    }
  };

接口的實現方法如下:

//讀取
 @Override public Object read(JsonReader in) throws IOException {
    JsonToken token = in.peek();//查看元素類型
    switch (token) {
    case BEGIN_ARRAY://數組
      List<Object> list = new ArrayList<Object>();
      in.beginArray();//開始標識
      while (in.hasNext()) {//還有下一個元素?
        list.add(read(in));
      }
      in.endArray();//結束標識
      return list;

    case BEGIN_OBJECT://對象
      Map<String, Object> map = new LinkedTreeMap<String, Object>();
      in.beginObject();
      while (in.hasNext()) {
        map.put(in.nextName(), read(in));
      }
      in.endObject();
      return map;

    case STRING:
      return in.nextString();

    case NUMBER:
      return in.nextDouble();

    case BOOLEAN:
      return in.nextBoolean();

    case NULL:
      in.nextNull();
      return null;

    default:
      throw new IllegalStateException();
    }
  }

  //寫爲json
  @Override public void write(JsonWriter out, Object value) throws IOException {
    if (value == null) {
      out.nullValue();
      return;
    }
    //gson.getAdapter獲取對應類型的TypeAdapter
    TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
    if (typeAdapter instanceof ObjectTypeAdapter) {
      out.beginObject();
      out.endObject();
      return;
    }

    typeAdapter.write(out, value);
  }

關於TypeAdapter這裏就不繼續深究了,預定義的TypeAdapter實在太多了。其中JsonAdapterAnnotationTypeAdapterFactory用來處理@JsonAdapter註解,ReflectiveTypeAdapterFactory用來反射獲取字段等等。

getAdapter

將Java對象序列化爲Json字符串時,需要調用gson.getAdapter(XX)來獲取相應類型的轉換器,然後按照TypeAdapter中規定好的規則進行序列化/反序列化。

 public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    //從typeTokenCache的Map中取出
    //NULL_KEY_SURROGATE表示Object類型
    TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
    if (cached != null) {
      return (TypeAdapter<T>) cached;
    }

   //threadCalls是一箇中間線程變量
    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
    boolean requiresThreadLocalCleanup = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
      calls.set(threadCalls);
      requiresThreadLocalCleanup = true;
    }

     //取回FutureTypeAdapter,FutureTypeAdapter是一個代理TypeAdapter
    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
    if (ongoingCall != null) {
      return ongoingCall;
    }

    try {
    //new FutureTypeAdapter
      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
      threadCalls.put(type, call);//放入線程變量中

     //從工廠中遍歷,加入typeTokenCache
      for (TypeAdapterFactory factory : factories) {
        TypeAdapter<T> candidate = factory.create(this, type);//create創造實例
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
      throw new IllegalArgumentException("GSON cannot handle " + type);
    } finally {
      threadCalls.remove(type);

      if (requiresThreadLocalCleanup) {
        calls.remove();
      }
    }
  }

以上源碼的流程是這樣的。首先從typeTokenCache中獲取看看有沒有相應類型的Adapter,第一次肯定沒有,然後利用中間變量FutureTypeAdapter,從工廠中遍歷去取,然後放入typeTokenCache中。FutureTypeAdapter是一個代理TypeAdapter,內部還是原TypeAdapter進行處理。

toJson

終於快接近尾聲了,現在來看toJson方法的內部原理,到底是怎麼一步步將對象轉爲Json字符串的。最終調用的是toJson(Object src, Type typeOfSrc, JsonWriter writer)方法,通過相應的Adapter進行讀寫。

  //傳入一個Object
  public String toJson(Object src) {
    if (src == null) {
      return toJson(JsonNull.INSTANCE);
    }
    return toJson(src, src.getClass());//重載調用
  }

  //重載方法
  public String toJson(Object src, Type typeOfSrc) {
    StringWriter writer = new StringWriter();
    toJson(src, typeOfSrc, writer);
    return writer.toString();//返回String
  }

//將StringWriter轉爲JsonWriter。
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
    try {
      JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));//轉爲JsonWriter。
      toJson(src, typeOfSrc, jsonWriter);
    } catch (IOException e) {
      throw new JsonIOException(e);
    }
  }


  public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
    //調用getAdapter獲取相應類型的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 {
      //調用adapter的write方法
      ((TypeAdapter<Object>) adapter).write(writer, src);
    } catch (IOException e) {
      throw new JsonIOException(e);
    } finally {
      writer.setLenient(oldLenient);
      writer.setHtmlSafe(oldHtmlSafe);
      writer.setSerializeNulls(oldSerializeNulls);
    }
  }

fromJson

  //傳入一個json字符串和一個轉換類型。
  public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);//調用重載
    return Primitives.wrap(classOfT).cast(object);//如果是基本數據類型還會被包裝返回(即 int => Integer)
  }

 //重載方法 
    public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    StringReader reader = new StringReader(json);//使用StringReader包裝
    T target = (T) fromJson(reader, typeOfT);
    return target;
  }

  //重載方法,將StringReader轉爲JsonReader
  public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    JsonReader jsonReader = newJsonReader(json);
    T object = (T) fromJson(jsonReader, typeOfT);
    assertFullConsumption(object, jsonReader);//判斷是不是讀完了
    return object;
  }

 //最終方法
 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    boolean oldLenient = reader.isLenient();
    reader.setLenient(true);
    try {
      reader.peek();
      isEmpty = false;
      //轉爲TypeToken
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      //獲取TypeAdapter
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      //調用TypeAdapter的read
      T object = typeAdapter.read(reader);
      return object;
    } catch (EOFException e) {
     //..
     //省略部分源碼
    } finally {
      reader.setLenient(oldLenient);
    }
  }

GsonBuilder

GsonBuilder用來對Gson進行配置,比如註冊TypeAdapter等等。最後調用create()返回Gson對象。
image_1amn76ilalcs2usk3t1mjddmm9.png-55.7kB

最後

Gson內部的Json與Java間的轉換依賴於各類的TypeAdapter,通過registerTypeAdapter可以註冊新的類型轉換器,實例創造器的等。越先註冊的優先級就越高。當然,如果你沒有註冊Adapter,對於自定義的對象一般參與轉換的Adapter是ReflectiveTypeAdapterFactory。有興趣的請自行閱讀源碼。
registerTypeAdapter的源碼如下:

  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
   //校驗參數
    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
        || typeAdapter instanceof JsonDeserializer<?>
        || typeAdapter instanceof InstanceCreator<?>
        || typeAdapter instanceof TypeAdapter<?>);
     //實例構造器   
    if (typeAdapter instanceof InstanceCreator<?>) {
      instanceCreators.put(type, (InstanceCreator) typeAdapter);
    }
    //序列化/反序列化
    if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
      TypeToken<?> typeToken = TypeToken.get(type);
      factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
    }
    //TypeAdapter
    if (typeAdapter instanceof TypeAdapter<?>) {
      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
  }

本期解讀到此結束,由於篇幅問題,Gson中預定義的TypeAdapter實在太多就不進行分析了。

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