Gson原始碼解析和它的設計模式

拉丁吳發表於2019-03-21

前言

之前一段時間,準備把糗百的專案中json解析的模組中的原生Json解析換成gson解析,工作比較繁雜,坑多,因此為了防止出錯,我還對Gson做了一個原始碼分析。這一篇就是Gson原始碼分析的總結,同時對Gson內部運用的設計模式也進行了總結,相信瞭解了它的原始碼和執行機制,對於使用Gson的使用會更有幫助。

Gson原始碼解析和它的設計模式

Gson簡介

Gson原始碼解析和它的設計模式

Gson,就是幫助我們完成序列化和反序列化的工作的一個庫。

  • 日常用法
        
        UserInfo userInfo = getUserInfo();
        Gson gson = new Gson();
        String jsonStr = gson.toJson(userInfo); // 序列化
        UserInfo user = gson.fromJson(jsonStr,UserInfo.class);  // 反序列化
        
複製程式碼

實際上我們用的最多的是Gson的反序列化,主要在解析伺服器返回的json串。因此,後面的文章也會以Gson中的反序列化的過程為主來分析程式碼。

在分析之前,我們先做個簡單的猜想,要如何實現反序列化的流程的,Gson大體會做一下這三件事:

  • 反射建立該型別的物件
  • 把json中對應的值賦給物件對應的屬性
  • 返回該物件。

事實上,Gson想要把json資料反序列化基本都逃不掉這三個步驟,但是這三個步驟就像小品裡分三步把大象裝進冰箱一樣。我們知道最複雜的一步就是把大象裝進去,畢竟,開冰箱門或者關冰箱門大家都會的嘛。在Gson中,複雜的就是怎樣把json中對應資料放入對應的屬性中。而這個問題的答案就是Gson的TypeAdapter。

Gson核心:TypeAdapter

TypeAdapter是Gson的核心,它的意思是型別介面卡,而說到介面卡,大家都會想到介面卡模式,沒錯,這個TypeAdapter的設計這確實是一個介面卡模式,因為Json資料介面和Type的介面兩者是無法相容,因此TypeAdapter就是來實現相容,把json資料讀到Type中,把Type中的資料寫入到Json裡。

Gson原始碼解析和它的設計模式

public abstract class TypeAdapter<T> {
 // JsonWriter代表Json資料,T則是對應的Type的物件
  public abstract void write(JsonWriter out, T value) throws IOException;
 // JsonWriter代表Json資料,T則是對應的Type的物件
  public abstract T read(JsonReader in) throws IOException;
  ...
  ...
  ...
}

複製程式碼

簡單而言,TypeAdapter的作用就是針對Type進行適配,保證把json資料讀到Type中,或者把Type中的資料寫入到Json裡

Type和TypeAdapter的對應關係

Gson會為每一種型別建立一個TypeAdapter,同樣的,每一個Type都對應唯一一個TypeAdapter

而所有Type(型別),在Gson中又可以分為基本型別和複合型別(非基本型別)

  • 基本型別(Integer,String,Uri,Url,Calendar...):這裡的基本型別不僅包括Java的基本資料型別,還有很多其他的資料型別
  • 複合型別(非基本型別):即除了基本型別之外的型別,往往是我們自定義的一些業務相關的JavaBean,比如User,Article.....等等。

這裡的基本型別和複合型別(非基本型別)是筆者定義的詞彙,因為這樣定義對於讀者理解Gson原始碼和執行機制更有幫助。

Gson原始碼解析和它的設計模式

如上圖,每一種基本型別都會建立一個TypeAdapter來適配它們,而所有的複合型別(即我們自己定義的各種JavaBean)都會由ReflectiveTypeAdapter來完成適配

TypeAdapter和Gson執行機制

既然講到了每種Type都有對應的TypeAdapter,那麼為什麼說TypeAdapter是Gson的核心呢?我們可以看看Gson到底是如何實現Json解析的呢,下圖是Gson完成json解析的抽象簡化的流程圖:

Gson原始碼解析和它的設計模式

如上圖,如果是基本型別,那麼對應的TypeAdapter就可以直接讀寫Json串,如果是複合型別,ReflectiveTypeAdapter會反射建立該型別的物件,並逐個分析其內部的屬性的型別,然後重複上述工作。直至所有的屬性都是Gson認定的基本型別並完成讀寫工作。

TypeAdapter原始碼分析

當型別是複合型別的時候,Gson會建立ReflectiveTypeAdapter,我們可以看看這個Adapter的原始碼:

// 建立ReflectiveTypeAdapter
new Adapter<T>(constructor, getBoundFields(gson, type, raw));

...
...

/**
* ReflectiveTypeAdapter是ReflectiveTypeAdapterFactory的內部類,其實際的類名就是Adapter
* 本文只是為了區別其他的TypeAdapter而叫它 ReflectiveTypeAdapter
**/
public static final class Adapter<T> extends TypeAdapter<T> {
    // 該複合型別的構造器,用於反射建立物件
    private final ObjectConstructor<T> constructor;
    // 該型別內部的所有的Filed屬性,都通過map儲存起來
    private final Map<String, BoundField> boundFields;

    Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
      this.constructor = constructor;
      this.boundFields = boundFields;
    }

 //JsonReader是Gson封裝的對Json相關的操作類,可以依次讀取json資料
 // 類似的可以參考Android封裝的對XML資料解析的操作類XmlPullParser
    @Override public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }

      T instance = constructor.construct();

      try {
        in.beginObject();  // 從“{”開始讀取
        while (in.hasNext()) {
          String name = in.nextName(); //開始逐個讀取json串中的key
          BoundField field = boundFields.get(name); // 通過key尋找對應的屬性
          if (field == null || !field.deserialized) {
            in.skipValue();
          } else {
            field.read(in, instance); // 將json串的讀取委託給了各個屬性
          }
        }
      } catch (IllegalStateException e) {
        throw new JsonSyntaxException(e);
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      }
      in.endObject(); // 到對應的“}”結束
      return instance;
    }
    ...
    ...
  }
  
複製程式碼

Gson內部並沒有ReflectiveTypeAdapter這個類,它其實際上是ReflectiveTypeAdapterFactory類一個名叫Adapter的內部類,叫它ReflectiveTypeAdapter是為了表意明確。

我們看到,ReflectiveTypeAdapter內部會首先建立該型別的物件,然後遍歷該物件內部的所有屬性,接著把json傳的讀去委託給了各個屬性。

被委託的BoundField內部又是如何做的呢?BoundField這個類,是對Filed相關操作的封裝,我們來看看BoundField是如何建立的,以及內部的工作原理。

// 建立ReflectiveTypeAdapter getBoundFields獲取該型別所有的屬性
new Adapter<T>(constructor, getBoundFields(gson, type, raw));

...
...


private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
    // 建立一個Map結構,存放所有的BoundField
    Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
    if (raw.isInterface()) {
      return result;
    }

    Type declaredType = type.getType();
    while (raw != Object.class) { // 如果型別是Object則結束迴圈
      Field[] fields = raw.getDeclaredFields(); // 獲取該型別的所有的內部屬性
      for (Field field : fields) {
        boolean serialize = excludeField(field, true);
        boolean deserialize = excludeField(field, false);
        if (!serialize && !deserialize) {
          continue;
        }
        accessor.makeAccessible(field);
        Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
        List<String> fieldNames = getFieldNames(field); // 獲取該Filed的名字(Gson通過註解可以給一個屬性多個解析名)
        BoundField previous = null;
        for (int i = 0, size = fieldNames.size(); i < size; ++i) {
          String name = fieldNames.get(i);
          // 多個解析名,第一作為預設的序列化名稱
          if (i != 0) serialize = false; // only serialize the default name
          // 建立BoundField
          BoundField boundField = createBoundField(context, field, name,
              TypeToken.get(fieldType), serialize, deserialize);
        // 將BoundField放入Map中,獲取被替換掉的value(如果有的話)
          BoundField replaced = result.put(name, boundField);
          // 做好記錄
          if (previous == null) previous = replaced;
        }
        if (previous != null) {
        // 如果previous != null證明出現了兩個相同的Filed name,直接丟擲錯誤
        // 注:Gson不允許定義兩個相同的名稱的屬性(父類和子類之間可能出現)
          throw new IllegalArgumentException(declaredType
              + " declares multiple JSON fields named " + previous.name);
        }
      }
      type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
      raw = type.getRawType(); // 獲取父類型別,最終會索引到Object.因為Object是所有物件的父類
    }
    return result;
  }


複製程式碼

上面這段程式碼的主要工作就是,找到該型別內部的所有屬性,並嘗試逐一封裝成BoundField。

// 根據每個Filed建立BoundField(封裝Filed讀寫操作)
  private ReflectiveTypeAdapterFactory.BoundField createBoundField(
      final Gson context, final Field field, final String name,
      final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
      // 是否是原始資料型別 (int,boolean,float...)
    final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
    ...
    ...
    if (mapped == null){
        // Gson嘗試獲取該型別的TypeAdapter,這個方法我們後面也會繼續提到。
        mapped = context.getAdapter(fieldType);
    }
    // final變數,便於內部類使用
    final TypeAdapter<?> typeAdapter = mapped;
    return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
      ...
      ...
      // ReflectiveTypeAdapter委託的Json讀操作會呼叫到這裡
      @Override void read(JsonReader reader, Object value)
          throws IOException, IllegalAccessException {
          // 通過該屬性的型別對應的TypeAdapter嘗試讀取json串
          //如果是基礎型別,則直接讀取,
          //如果是複合型別則遞迴之前的流程
        Object fieldValue = typeAdapter.read(reader);
        if (fieldValue != null || !isPrimitive) {
          field.set(value, fieldValue); //更新filed值 
        }
      }
      @Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
        if (!serialized) return false;
        Object fieldValue = field.get(value);
        return fieldValue != value; // avoid recursion for example for Throwable.cause
      }
    };
  }
    
    
複製程式碼

假設該複合型別中所有的屬性的型別是String,則屬性所對應的TypeAdapter以及其讀寫方式如下:


  public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
    @Override
    public String read(JsonReader in) throws IOException {
      JsonToken peek = in.peek();   // 獲取下一個jsontoken而不消耗它
      if (peek == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      /* coerce booleans to strings for backwards compatibility */
      if (peek == JsonToken.BOOLEAN) {
        return Boolean.toString(in.nextBoolean()); // 如果時布林值,則轉化為String
      }
      return in.nextString();   // 從json串中獲取這個String型別的value並消耗它
    }
    @Override
    public void write(JsonWriter out, String value) throws IOException {
      out.value(value); // 不做任何處理直接寫入Json串
    }
  }
  
複製程式碼

到這裡,關於Gson的TypeAdapter的原理也就講得差不多了,回顧一下,因為Type有兩類,對應的TypeAdapter也有兩類,一類是ReflectiveTypeAdapter,針對複合型別,它的作用是把複合型別拆解成基本型別,另一類是針對基本型別的TypeAdapter,實現對應基本型別的Json串讀寫工作。而Gson本質上就是按照這兩類TypeAdapter來完成Json解析的。

Gson原始碼解析和它的設計模式

可以說,到這裡,我們現在對Gson的基本工作流程有了一個基本的認識。

再一次分析Gson的執行邏輯

事實上,文章到這裡結合上面的原始碼剖析和簡化流程圖,我們已經可以比較比較真實的分析出Gson的執行邏輯了。

Gson反序列化的日常用法:

        
        UserInfo userInfo = getUserInfo();
        Gson gson = new Gson();
        String jsonStr = getJsonData();
        UserInfo user = gson.fromJson(jsonStr,UserInfo.class);  // 反序列化
        
複製程式碼

gson.fromJson(jsonStr,UserInfo.class)方法內部真實的程式碼執行流程大致如下:

  • 對jsonStr,UserInfo.class這兩個資料進行封裝
  • 通過UserInfo.class這個Type來獲取它對應的TypeAdapter
  • 拿到對應的TypeAdapter(ReflectiveTypeAdapterFactor),並執行讀取json的操作
  • 返回UserInfo這個型別的物件。

我們描述的這個流程和Gson程式碼真實的執行流程已經沒太大的區別了。

TypeAdapter的建立與工廠模式

Gson中除了介面卡模式之外最重要的設計模式,可能就是工廠模式吧。因為Gson中眾多的TypeAdapter都是通過工廠模式統一建立的:


public interface TypeAdapterFactory {
    // 建立TypeAdapter的介面
  <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}


複製程式碼

我們可以看看ReflectiveTypeAdapterFactory的實現


// ReflectiveTypeAdapterFactory的實現
public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {

  @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> raw = type.getRawType();
    // 只要是Object的子類,就能匹配上
    if (!Object.class.isAssignableFrom(raw)) {
     // it's a primitive!
      return null; 
    }
    ObjectConstructor<T> constructor = constructorConstructor.get(type);
    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
  }
  }
  
  
複製程式碼

Gson在其構造方法中,就提前把所有的TypeAdapterFactory放在快取列表中。


  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, String datePattern, int dateStyle,
      int timeStyle, List<TypeAdapterFactory> builderFactories,
      List<TypeAdapterFactory> builderHierarchyFactories,
      List<TypeAdapterFactory> factoriesToBeAdded) {
    ...
    ...
    ...

    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();

    // built-in type adapters that cannot be overridden
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);

    // the excluder must precede all adapters that handle user-defined types
    factories.add(excluder);

    // users type adapters
    factories.addAll(factoriesToBeAdded);

    // type adapters for basic platform types
    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);

    // type adapters for composite and user-defined types
    factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
    this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
    factories.add(jsonAdapterFactory);
    factories.add(TypeAdapters.ENUM_FACTORY);
    // 注意,ReflectiveTypeAdapterFactor是要最後新增的
    factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));

    this.factories = Collections.unmodifiableList(factories);
  }
  
複製程式碼

這裡我們能夠看到,ReflectiveTypeAdapterFactor最後被新增進去的,因為這裡的新增順序是有講究的。我們看看getAdapter(type)方法就能知道。

getAdapter(type)這個方法就是gson通過type尋找到對應的TypeAdapter,這是Gson中非常重要的一個方法。


// 通過Type獲取TypeAdapter
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {

    try {
      
      // 遍歷快取中所有的TypeAdapterFactory,
      for (TypeAdapterFactory factory : factories) {
      //如果型別匹配,則create()將會返回一個TypeAdapter,否則為nulll
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
            // candidate不為null,證明找到型別匹配的TypeAdapter.
          return candidate;
        }
      }
      throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
    }
  }
  
複製程式碼

ReflectiveTypeAdapterFactory之所以在快取列表的最後一個,就是因為它能匹配幾乎任何型別,因此,我們為一個型別遍歷時,只能先判斷它是不是基本型別,如果都不成功,最後再使用ReflectiveTypeAdapterFactor進行判斷。

這就是Gson中用到的工廠模式。

關於程式碼的難點

我們重新回到getAdapter(type)這個方法,這個方法裡面有一些比較難理解的程式碼


// 通過Type獲取TypeAdapter
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    // typeTokenCache是Gson的一個Map型別的快取結構
    // 0,首先嚐試從快取中獲取是否有對應的TypeAdapter
    TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
    if (cached != null) {
      return (TypeAdapter<T>) cached;
    }

    // 1,alls 是Gson內部的ThreadLocal變數,用於儲存一個Map物件
    // map物件也快取了一種FutureTypeAdapter
    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
    boolean requiresThreadLocalCleanup = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
      calls.set(threadCalls);
      requiresThreadLocalCleanup = true;
    }

    //2,如果從ThreadLocal內部的Map中找到快取,則直接返回
    // the key and value type parameters always agree
    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
    if (ongoingCall != null) {
      return ongoingCall;
    }

    try {
    建立一個FutureTypeAdapter
      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
      // 快取
      threadCalls.put(type, call);

      for (TypeAdapterFactory factory : factories) {
      // 遍歷所有的TypeAdapterFactory
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
        // 3, 設定委託的TypeAdapter
          call.setDelegate(candidate);
          // 快取到Gson內部的Map中,
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
      //如果遍歷都沒有找到對應的TypeAdapter,直接丟擲異常
      throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
    } finally {
    // 4,移除threadCalls內部快取的 FutureTypeAdapter
      threadCalls.remove(type);

      if (requiresThreadLocalCleanup) {
      //ThreadLocal移除該執行緒環境中的Map
        calls.remove();
      }
    }
  }
複製程式碼

上述程式碼比較難以理解的地方我標註了序號,用於後面解釋程式碼

方法裡出現了FutureTypeAdapter這個TypeAdapter,似乎很奇怪,因為它沒有FutureTypeAdapterFactory這個工廠類,我們先來看看 FutureTypeAdapter的內部構造


  static class FutureTypeAdapter<T> extends TypeAdapter<T> {
    private TypeAdapter<T> delegate;

    public void setDelegate(TypeAdapter<T> typeAdapter) {
      if (delegate != null) {
        throw new AssertionError();
      }
      delegate = typeAdapter;
    }

    @Override public T read(JsonReader in) throws IOException {
      if (delegate == null) {
        throw new IllegalStateException();
      }
      return delegate.read(in);
    }

    @Override public void write(JsonWriter out, T value) throws IOException {
      if (delegate == null) {
        throw new IllegalStateException();
      }
      delegate.write(out, value);
    }
  }
  
  
複製程式碼

這是一個明顯的委派模式(也可稱為代理模式)的包裝類。我們都知道委託模式的功能是:隱藏程式碼具體實現,通過組合的方式同樣的功能,避開繼承帶來的問題。但是在這裡使用委派模式似乎並不是基於這些考慮。而是為了避免陷入無限遞迴導致對棧溢位的崩潰。

為什麼這麼說呢?我們來舉個例子:

// 定義一個帖子的實體
public class Article {
    // 表示帖子中連結到其他的帖子
    public Article linkedArticle;
    .....
    .....
    .....
    
    
}

複製程式碼

Article型別中有一個linkedArticle屬性,它的型別還是Article,根據我們之前總結的簡化流程圖:

Gson原始碼解析和它的設計模式

你會發現這裡有一個死迴圈,或者說無法終結的遞迴。為了避免這個問題,所以先建立一個代理類,等到遞迴遇到同樣的型別時直接複用返回,避免無限遞迴。也就是註釋2那段程式碼的用意,在註釋3處,再將建立成功的TypeAdapter設定到代理類中。就基本解決這個問題了。

當然說基本解決,是因為還要考慮多執行緒的環境,所以就出現了ThreadLocal這個執行緒區域性變數,這保證了它只會在單個執行緒中快取,而且會在單次Json解析完成後移出快取。見上文註釋1和註釋4。這是因為無限遞迴只會發生在單次Json解析中,而且Gson內部已經有了一個TypeAdapterde 全域性快取(typeTokenCache),見註釋0.

Gson原始碼解析和它的設計模式

潛在的遞迴迴圈: gson.getAdapter(type) ---> (ReflectiveTypeAdapterFactory)factory.create(this, type) ---> getBoundFields() ---> createBoundField() ---> (Gson)context.getAdapter(fieldType)

關於Gson自定義解析

上文只講到了Gson自己內部是如何實現Json解析的,其實Gson也提供了一些自定義解析的介面。主要是兩種:

  • 自己實現繼承TypeAdapter
  • 實現JsonSerializer/JsonDeserializer介面

那麼,兩者有什麼區別呢?

追求效率更高,選第一種,想要操作更簡單,實現更靈活,選第二種。

為什麼這麼說?舉個例子,假設我們需要為Article這個JavaBean自定義解析,如果我們選擇繼承TypeAdapter的話,需要先實現TypeAdapter,然後註冊。

    // 繼承TypeAdapter,實現抽象方法
    public class ArticleTypeAdapter extends TypeAdapter<Article>{

        @Override
        public void write(JsonWriter out, Article value) throws IOException {
            // 實現把Article中的實體資料的寫入到JsonWriter中,實現序列化
        }

        @Override
        public Article read(JsonReader in) throws IOException {
            // 需要建立Article物件
            // 把 JsonReader中的json串讀出來,並設定到Article物件中
            return null;
        }
    }
    
    ...
    ...
    // 註冊
    Gson mGson = new GsonBuilder()
    .registerTypeAdapter(Article.class, new ArticleTypeAdapter<>())//實際上註冊到Gson的factories列表中
    .create();
    
    
複製程式碼

這樣就實現了自定義的Json解析,這種方式的讀寫效率很高,但是不太靈活,因為必須要同時實現序列化和反序列化的工作。

而實現JsonSerializer/JsonDeserializer介面這種方式相對更簡單

    //JsonSerializer(json序列話)/JsonDeserializer(反序列化)可按需實現
    public class ArticleTypeAdapter implements JsonDeserializer<Article> {
        @Override
        public Article deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
             // 需要建立Article物件
            // 並從JsonElement中把封裝好的Json資料結構讀出來,並設定到Article物件中
            return null;
        }
    }


    // 註冊
    Gson mGson = new GsonBuilder()
    .registerTypeAdapter(Article.class, new ArticleTypeAdapter<>())//實際上註冊到Gson的factories列表中
    .create();
    
複製程式碼

我們可以看到,兩者的區別,是後者更加靈活,序列化/返序列化可按需選擇,而且它使用了JsonElement對Json資料進行再封裝,從而使我們操作Json資料更加簡單。不過正是因為使用了 JsonElement這種對Json資料再封裝的類,而不是更加原始的JsonReader導致了程式碼執行效率的降低。

Gson原始碼解析和它的設計模式

如上圖所示,本質上就是多了一箇中間層,導致解析效率的降低。不過話說回來,只要不是非常大批量複雜結構的連續解析,這種效率差異我們可以忽略不計,因此日常的開發,大家通過JsonSerializer/JsonDeserializer介面來實現自定義解析是一個相對更好的選擇。

相關文章