我個人覺得 Gson是一個非常優秀的json解析開源項目,效率高, 代碼結構也非常的清晰 。給使用者提供了非常好的用戶體驗。封裝也非常的優雅 。因此, 我對Gson的研究充滿了興趣,這裏順帶提一下我前兩天發現的一個非常不錯的Rest封裝框架: Retrofit, 我不知道知道的人有多少, 我之前一直使用的是自己寫的Rest封裝,代碼的 複用 性也不強, 而Retrofit卻把這樣東西做了極致。我 將在後面的文章中研究它。 你可能很少不了 @子墨 這種想到哪寫到哪的做法。不過如果 你看 的順眼就不妨跟 着 這不成文的節奏走吧,因爲我不打算改變我的寫作方式。
回到 正題, Gson主要分成兩部分 ,一個就是 數據拆解,一個是數據封裝。這兩個概念我都是基於JSON 層的概念而言。這樣JSON對象的拆解 的結果就是JAVA對象,而所謂的封裝就是將一個OO的對象封裝成爲一個json對象。當然json對象可以理解爲就是一個字符串。那麼實際上Gson做的事情就是完成St ring到JAVA 對象的映射。我們建立起這個概念以後我們就能明白我們的 研究主要分成兩個大部分。前幾個系列我們會先說String到JAVA對象之間的映射。
我們 導入Gson的源碼 發現代碼比 預想的少的多, 分包也很明確。你是不是瞬間對Google公司崇敬起來。我們明白, 如果要對 系統之上構建一個封裝的 話,用的就是門面模式 , 關於門面模式的說法,大家可以參考 我的一篇文章:
子墨對酒《三國殺》裏論模式(二)門面模式
構造門面的 包名一般都是所有系統的頂層包。我們看到 com.google.gson 就很好的封裝了這些門面。也就是說我們要跟系統打交道, 實際上就是要跟這些門面打 交道。
String 到 對象的主要門面是Gson .java。 裏面提供了各種各樣的方法方便你調用。
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<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT); TypeAdapter<T> typeAdapter = getAdapter(typeToken); T object = typeAdapter.read(reader); System.out.println("object = "+object); return object; } catch (EOFException e) { /* * For compatibility with JSON 1.5 and earlier, we return null for empty * documents instead of throwing. */ if (isEmpty) { return null; } throw new JsonSyntaxException(e); } catch (IllegalStateException e) { throw new JsonSyntaxException(e); } catch (IOException e) { // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException throw new JsonSyntaxException(e); } finally { reader.setLenient(oldLenient); } }我們能比較清晰的看到, 實際上對於數據的轉換, 是由:TypeAdapter<T> typeAdapter這個對象來完成的,我們可以從名字看出它本質上一個適配器。關於適配器模式大家可以參考 我的 一篇文章:
子墨對酒《三國殺》裏論模式(三)適配器模式
爲什麼需要適配 器呢 ?我們粘出一個適配器的代碼相信大家就能瞭解了:public final class DateTypeAdapter extends TypeAdapter<Date> { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null; } }; private final DateFormat enUsFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US); private final DateFormat localFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT); private final DateFormat iso8601Format = buildIso8601Format(); private static DateFormat buildIso8601Format() { DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); return iso8601Format; } @Override public Date read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return deserializeToDate(in.nextString()); } private synchronized Date deserializeToDate(String json) { try { return localFormat.parse(json); } catch (ParseException ignored) { } try { return enUsFormat.parse(json); } catch (ParseException ignored) { } try { return iso8601Format.parse(json); } catch (ParseException e) { throw new JsonSyntaxException(json, e); } } @Override public synchronized void write(JsonWriter out, Date value) throws IOException { if (value == null) { out.nullValue(); return; } String dateFormatAsString = enUsFormat.format(value); out.value(dateFormatAsString); } }我們可以發現, 對於傳入的數據類型來說, Gson並不知道什麼時候要用它來適配,它的適配要完全依賴於這些適配器。當你傳入的是適配器 判斷條件所需的字節碼條件的時候, 就將採用該種適配器來進行適配 。 但是令人非常遺憾的是: 在gson這個門面的設計的時候,並沒有提供接口用來factory的擴展,這樣 你就得完全按照Gson的那一套規則 。 而且對於 一個對象的生成採用成文的規定,完全沒有給外界修改的權利。但是這 並不會影響G son作爲一個出色的項目,優雅的代碼結構而存在。
代碼中讓我們非常崩潰的就是大量的匿名類,不過Gson似乎也意識到了這個問題:
public static <TT> TypeAdapterFactory newFactory( final Class<TT> type, final TypeAdapter<TT> typeAdapter) { return new TypeAdapterFactory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null; } @Override public String toString() { return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; } }; }我們發現它使用通用的模板來生成類,無可厚非。而且也複寫了toString爲了方便調試。我 寫了一段代碼來做測試:
/**
* @author 非子墨
* */
public class TestGson {
public static class User{
public String name;
public int age;
public Info info;
@Override
public String toString() {
// TODO Auto-generated method stub
return "["+name+":"+info.msg+"]";
}
}
public static class Info{
public Info() {}
String msg;
}
public static void main(String[] args) {
String gsonValue = "{name:'非子墨',age:23,info:{msg:'I am a student!'}}";
Gson gson = new Gson() ;
User user = gson.fromJson(gsonValue, User.class);
System.out.println("user = "+user);
}
}
最後的運行結果是:
Gson:>>>Factory[type=java.lang.String,adapter=com.google.gson.internal.bind.TypeAdapters$13@398020cc]
Gson:>>>Factory[type=java.lang.Integer+int,adapter=com.google.gson.internal.bind
Gson:>>>com.google.gson.internal.bind.ReflectiveTypeAdapterFactory@6030e280
Gson:create type = com.test.TestGson$Info
Gson:>>>com.google.gson.internal.bind.ReflectiveTypeAdapterFactory@6030e280
Gson:create type = com.test.TestGson$User
這裏面我 加了一些log ,我們可以通過這些log能很明白的理解Gson的總體解析結構,就是順序解析, 如果是對象的 話是通過
ReflectiveTypeAdapterFactory
這個類生成的Adapter對象來生成。我們也會發現,作爲最終要解析成的對象User, 實際上 是最後解析的, 因此我們推測,解析完 內部對象以後採用組合的方式來注入。至於組合模式,將會在我以後關於 <三國殺>設計模式的文章 中 補充到。
由於時間的 關係 本系列文章將會分成多個小節來寫, 下一篇我希望還是像Proguard源碼分析一樣 先從Json的數據讀 取開始, 主要的類是:
JsonReader .java
待續。。。